浏览代码

基础框架

tjf 2 月之前
父节点
当前提交
93a2cbc461
共有 69 个文件被更改,包括 3173 次插入905 次删除
  1. 0 185
      README.md
  2. 5 5
      pom.xml
  3. 5 5
      ruoyi-admin/pom.xml
  4. 17 6
      ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java
  5. 10 13
      ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
  6. 114 0
      ruoyi-admin/src/main/java/org/dromara/web/controller/CommonController.java
  7. 33 3
      ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
  8. 4 2
      ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java
  9. 2 5
      ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
  10. 28 63
      ruoyi-admin/src/main/resources/application-dev.yml
  11. 15 55
      ruoyi-admin/src/main/resources/application-prod.yml
  12. 3 1
      ruoyi-admin/src/main/resources/application.yml
  13. 64 61
      ruoyi-admin/src/main/resources/i18n/messages.properties
  14. 16 14
      ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
  15. 4 0
      ruoyi-admin/src/main/resources/logback-plus.xml
  16. 28 0
      ruoyi-common/ruoyi-common-core/pom.xml
  17. 62 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/BoManConfig.java
  18. 35 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ServerConfig.java
  19. 27 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/properties/BoManProperties.java
  20. 81 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/Constants.java
  21. 4 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java
  22. 11 5
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java
  23. 6 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java
  24. 26 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/UtilException.java
  25. 61 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileUploadException.java
  26. 81 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/InvalidExtensionException.java
  27. 103 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ImageSizeUtil.java
  28. 111 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/PwdCheckUtil.java
  29. 234 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/FileUploadUtils.java
  30. 11 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java
  31. 49 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/uuid/IdUtils.java
  32. 88 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/uuid/Seq.java
  33. 486 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/uuid/UUID.java
  34. 4 0
      ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessType.java
  35. 10 1
      ruoyi-common/ruoyi-common-oss/pom.xml
  36. 2 2
      ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/constant/OssConstant.java
  37. 4 0
      ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java
  38. 1 1
      ruoyi-modules/pom.xml
  39. 1 1
      ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml
  40. 2 2
      ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
  41. 4 4
      ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm
  42. 17 16
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java
  43. 3 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java
  44. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java
  45. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java
  46. 3 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java
  47. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
  48. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java
  49. 3 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssConfigController.java
  50. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java
  51. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java
  52. 39 4
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java
  53. 7 7
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java
  54. 3 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java
  55. 3 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java
  56. 21 9
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java
  57. 31 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserTenant.java
  58. 4 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
  59. 29 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserTenantBo.java
  60. 35 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserTenantVo.java
  61. 4 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/UserInfoVo.java
  62. 15 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserTenantMapper.java
  63. 23 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
  64. 69 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserTenantService.java
  65. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java
  66. 65 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
  67. 124 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserTenantServiceImpl.java
  68. 7 0
      ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserTenantMapper.xml
  69. 803 404
      script/sql/ry_vue_5.X.sql

+ 0 - 185
README.md

@@ -1,185 +0,0 @@
-<img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%">
-<div style="height: 10px; clear: both;"></div>
-
-- - -
-## 平台简介
-
-[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus)
-[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
-[![Star](https://gitcode.com/dromara/RuoYi-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Vue-Plus)
-[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
-[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-<br>
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.3.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
-[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]()
-[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
-[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
-
-> Dromara RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
-
-> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
-活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
-
-> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
-
-> 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)<br>
-> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
-
-> 文档地址: [plus-doc](https://plus-doc.dromara.org)
-
-## 赞助商
-
-MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
-CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
-数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
-引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
-<font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br>
-[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
-
-# 本框架与RuoYi的功能差异
-
-| 功能          | 本框架                                                                                                               | RuoYi                                                                              |
-|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
-| 前端项目        | 采用 Vue3 + TS + ElementPlus 重写                                                                                     | 基于Vue2/Vue3 + JS                                                                   | 
-| 后端项目结构      | 采用插件化 + 扩展包形式 结构解耦 易于扩展                                                                                           | 模块相互注入耦合严重难以扩展                                                                     | 
-| 后端代码风格      | 严格遵守Alibaba规范与项目统一配置的代码格式化                                                                                        | 代码书写与常规结构不同阅读障碍大                                                                   |
-| Web容器       | 采用 Undertow 基于 XNIO 的高性能容器                                                                                        | 采用 Tomcat                                                                          |
-| 权限认证        | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展                                                                                  | Spring Security 配置繁琐扩展性极差                                                          |
-| 权限注解        | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式        | 只支持是否存在匹配                                                                          |
-| 三方鉴权        | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证                                                                               | 无                                                                                  |
-| 关系数据库支持     | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例)      | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换                                                    |
-| 缓存数据库       | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列                                                                             | Redis 简单 get set 支持                                                                |
-| Redis客户端    | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题              |
-| 缓存注解        | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存                                      | 需手动编写Redis代码逻辑                                                                     |
-| ORM框架       | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等                                             | 采用 Mybatis 基于XML需要手写SQL                                                            |
-| SQL监控       | 采用 p6spy 可输出完整SQL与执行时间监控                                                                                          | log输出 需手动拼接sql与参数无法快速查看调试问题                                                        |
-| 数据分页        | 采用 Mybatis-Plus 分页插件<br/>框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序                                                  | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好                               |
-| 数据权限        | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤<br/>只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色                                           | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展<br/>生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 |
-| 数据脱敏        | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件<br/>支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展                                        | 无                                                                                  |
-| 数据加解密       | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等                                              | 无                                                                                  |
-| 接口传输加密      | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性                                                                     | 无                                                                                  |
-| 数据翻译        | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现                   | 无                                                                                  |
-| 多数据源框架      | 采用 dynamic-datasource 支持市面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源            | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差                                                     |
-| 多数据源事务      | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚                                                                          | 不支持                                                                                |
-| 数据库连接池      | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下                                                                        | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般                                               |
-| 数据库主键       | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁                                                                  | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一                                                     |
-| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物                                                         | 无                                                                                  |
-| SSE推送       | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步                                                                               | 无                                                                                  |
-| 序列化         | 采用 Jackson Spring官方内置序列化 靠谱!!!                                                                                    | 采用 fastjson bugjson 远近闻名                                                           | 
-| 分布式幂等       | 参考美团GTIS防重系统简化实现(细节可看文档)                                                                                          | 手动编写注解基于aop实现                                                                      |
-| 分布式锁        | 采用 Lock4j 底层基于 Redisson                                                                                           | 无                                                                                  |
-| 分布式任务调度     | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等                                                                 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造                                                   | 
-| 文件存储        | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储                                                     | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应                                                    |
-| 云存储         | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家                                                                          | 不支持                                                                                |
-| 短信          | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用                                                                 | 不支持                                                                                |
-| 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                |
-| 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                | 
-| 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    |
-| Excel框架     | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                               | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           |
-| 工作流支持       | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能                                                                                   | 无                                                                                  |
-| 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            | 
-| 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  | 
-| 链路追踪        | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗<br/>用了它即可实时查看请求经过的每一处每一个节点                                            | 无                                                                                  |
-| 代码生成器       | 只需设计好表结构 一键生成所有crud代码与页面<br/>降低80%的开发量 把精力都投入到业务设计上<br/>框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成                    | 代码生成原生结构 只支持单数据源生成                                                                 |
-| 部署方式        | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼                                                                           | 原生jar部署 其他环境需手动下载安装 自行搭建                                                           | 
-| 项目路径修改      | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的                                                                              | 需要做很多改造 文档说明有限                                                                     |
-| 国际化         | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化                                                                     | 只提供基础功能 其他需自行编写扩展                                                                  |
-| 代码单例测试      | 提供单例测试 使用方式编写方法与maven多环境单测插件                                                                                      | 只提供基础功能 其他需自行编写扩展                                                                  |
-| Demo案例      | 提供框架功能的实际使用案例 单独一个模块提供了很多很全                                                                                       | 无                                                                                  |
-
-
-## 本框架与RuoYi的业务差异
-
-| 业务     | 功能说明                                                                 | 本框架 | RuoYi            |
-|--------|----------------------------------------------------------------------|-----|------------------|
-| 租户管理   | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等                                      | 支持  | 无                |
-| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等                                          | 支持  | 无                |
-| 客户端管理  | 系统内对接的所有客户端管理 如: pc端、小程序端等<br>支持动态授权登录方式 如: 短信登录、密码登录等 支持动态控制token时效 | 支持  | 无                |
-| 用户管理   | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等                                       | 支持  | 支持               |
-| 部门管理   | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限                                       | 支持  | 支持               |
-| 岗位管理   | 配置系统用户所属担任职务                                                         | 支持  | 支持               |
-| 菜单管理   | 配置系统菜单、操作权限、按钮权限标识等                                                  | 支持  | 支持               |
-| 角色管理   | 角色菜单权限分配、设置角色按机构进行数据范围权限划分                                           | 支持  | 支持               |
-| 字典管理   | 对系统中经常使用的一些较为固定的数据进行维护                                               | 支持  | 支持               |
-| 参数管理   | 对系统动态配置常用参数                                                          | 支持  | 支持               |
-| 通知公告   | 系统通知公告信息发布维护                                                         | 支持  | 支持               |
-| 操作日志   | 系统正常操作日志记录和查询 系统异常信息日志记录和查询                                          | 支持  | 支持               |
-| 登录日志   | 系统登录日志记录查询包含登录异常                                                     | 支持  | 支持               |
-| 文件管理   | 系统文件展示、上传、下载、删除等管理                                                   | 支持  | 无                |
-| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理                                       | 支持  | 无                |
-| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作                                                | 支持  | 支持               |
-| 定时任务   | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等                                      | 支持  | 仅支持任务与日志管理       |
-| 代码生成   | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载                              | 支持  | 仅支持单数据源          |
-| 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               |
-| 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 |
-| 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               |
-| 在线构建器  | 拖动表单元素生成相应的HTML代码。                                                   | 支持  | 支持               |
-| 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              |
-
-## 参考文档
-
-使用框架前请仔细阅读文档重点注意事项
-<br>
->[初始化项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
->>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
->
->[专栏与视频 入门必看](https://plus-doc.dromara.org/#/common/column)
->>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column)
->
->[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
->>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
->
->[如何加群](https://plus-doc.dromara.org/#/common/add_group)
->>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group)
->
->[参考文档 Wiki](https://plus-doc.dromara.org)
->>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
-
-## 软件架构图
-
-![Plus部署架构图](https://foruda.gitee.com/images/1678981882624240692/ae2a3f3e_1766278.png "Plus部署架构图.png")
-
-## 如何参与贡献
-
-[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
-
-## 捐献作者
-作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭  
-<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
-<img src="https://foruda.gitee.com/images/1678975801230205215/6f96229d_1766278.png" width="300px" height="450px" />
-
-## 演示图例
-
-|                                                                                            |                                                                                            |
-|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
-| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1735829153637063344/3c21fd4c_1419627.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1735829181303499815/4522cefa_1419627.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1735829377205259767/76a705d7_1419627.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1722959592856812900/e2d0d342_1419627.png "屏幕截图") |
-| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") |
-
-
-
-
-
-
-
-
-
-
-
-

+ 5 - 5
pom.xml

@@ -351,11 +351,11 @@
                 <version>${revision}</version>
             </dependency>
 
-            <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-demo</artifactId>
-                <version>${revision}</version>
-            </dependency>
+<!--            <dependency>-->
+<!--                <groupId>org.dromara</groupId>-->
+<!--                <artifactId>ruoyi-demo</artifactId>-->
+<!--                <version>${revision}</version>-->
+<!--            </dependency>-->
 
             <!--  工作流模块  -->
             <dependency>

+ 5 - 5
ruoyi-admin/pom.xml

@@ -81,11 +81,11 @@
             <artifactId>ruoyi-generator</artifactId>
         </dependency>
 
-        <!--  demo模块  -->
-        <dependency>
-            <groupId>org.dromara</groupId>
-            <artifactId>ruoyi-demo</artifactId>
-        </dependency>
+<!--        &lt;!&ndash;  demo模块  &ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>org.dromara</groupId>-->
+<!--            <artifactId>ruoyi-demo</artifactId>-->
+<!--        </dependency>-->
 
         <!--  工作流模块  -->
         <dependency>

+ 17 - 6
ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java

@@ -16,6 +16,7 @@ import me.zhyd.oauth.utils.AuthStateUtils;
 import org.dromara.common.core.constant.SystemConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.LoginBody;
+import org.dromara.common.core.domain.model.PasswordLoginBody;
 import org.dromara.common.core.domain.model.RegisterBody;
 import org.dromara.common.core.domain.model.SocialLoginBody;
 import org.dromara.common.core.utils.*;
@@ -31,10 +32,8 @@ import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.bo.SysTenantBo;
 import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysTenantVo;
-import org.dromara.system.service.ISysClientService;
-import org.dromara.system.service.ISysConfigService;
-import org.dromara.system.service.ISysSocialService;
-import org.dromara.system.service.ISysTenantService;
+import org.dromara.system.domain.vo.SysUserVo;
+import org.dromara.system.service.*;
 import org.dromara.web.domain.vo.LoginTenantVo;
 import org.dromara.web.domain.vo.LoginVo;
 import org.dromara.web.domain.vo.TenantListVo;
@@ -72,7 +71,7 @@ public class AuthController {
     private final ISysSocialService socialUserService;
     private final ISysClientService clientService;
     private final ScheduledExecutorService scheduledExecutorService;
-
+    private final ISysUserService userService;
 
     /**
      * 登录方法
@@ -96,6 +95,18 @@ public class AuthController {
         } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
             return R.fail(MessageUtils.message("auth.grant.type.blocked"));
         }
+        //查询用户的租户id
+        PasswordLoginBody loginBodyUser = JsonUtils.parseObject(body, PasswordLoginBody.class);
+        String username = loginBodyUser.getUsername();
+        SysUserVo sysUserVo = userService.selectUserByUserName(username);
+        String tenantId = sysUserVo.getTenantId();
+        //登录默认给一个租户id 000000
+        loginBody.setTenantId("000000");
+        //如果多租户开启且租户id是空
+        if (TenantHelper.isEnable() && StringUtils.isNotBlank(tenantId)) {
+            //给一个租户id
+            loginBody.setTenantId(sysUserVo.getTenantId());
+        }
         // 校验租户
         loginService.checkTenant(loginBody.getTenantId());
         // 登录
@@ -104,7 +115,7 @@ public class AuthController {
         Long userId = LoginHelper.getUserId();
         scheduledExecutorService.schedule(() -> {
             SseMessageDto dto = new SseMessageDto();
-            dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
+            dto.setMessage("欢迎登录物业管理系统");
             dto.setUserIds(List.of(userId));
             SseMessageUtils.publishMessage(dto);
         }, 5, TimeUnit.SECONDS);

+ 10 - 13
ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java

@@ -22,9 +22,6 @@ import org.dromara.common.ratelimiter.enums.LimitType;
 import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.common.web.config.properties.CaptchaProperties;
 import org.dromara.common.web.enums.CaptchaType;
-import org.dromara.sms4j.api.SmsBlend;
-import org.dromara.sms4j.api.entity.SmsResponse;
-import org.dromara.sms4j.core.factory.SmsFactory;
 import org.dromara.web.domain.vo.CaptchaVo;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
@@ -34,7 +31,6 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.time.Duration;
-import java.util.LinkedHashMap;
 
 /**
  * 验证码操作处理
@@ -53,17 +49,18 @@ public class CaptchaController {
 
     /**
      * 短信验证码
-     *
-     * @param phonenumber 用户手机号
+     * @param phonenumber 手机号码
+     * @param type 1:登录 2:注册 3:修改密码
+     * @return
      */
-    @RateLimiter(key = "#phonenumber", time = 60, count = 1)
-    @GetMapping("/resource/sms/code")
-    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
-        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
+    //@RateLimiter(key = "#phonenumber", time = 60, count = 1)
+    @GetMapping("/wuYe/resource/sms/code")
+    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber, String type) {
+        String key = GlobalConstants.SMS_CAPTCHA_CODE_KEY + type + ":" + phonenumber;
         String code = RandomUtil.randomNumbers(4);
         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
-        // 验证码模板id 自行处理 (查数据库或写死均可)
-        String templateId = "";
+/*        // 验证码模板id 自行处理 (查数据库或写死均可)
+        String templateId = "SMS_219525380";
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
         map.put("code", code);
         SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
@@ -71,7 +68,7 @@ public class CaptchaController {
         if (!smsResponse.isSuccess()) {
             log.error("验证码短信发送异常 => {}", smsResponse);
             return R.fail(smsResponse.getData().toString());
-        }
+        }*/
         return R.ok();
     }
 

+ 114 - 0
ruoyi-admin/src/main/java/org/dromara/web/controller/CommonController.java

@@ -0,0 +1,114 @@
+package org.dromara.web.controller;
+
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.config.BoManConfig;
+import org.dromara.common.core.config.ServerConfig;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.ImageSizeUtil;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.file.FileUploadUtils;
+import org.dromara.common.core.utils.file.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 通用请求处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/common")
+public class CommonController
+{
+    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
+
+
+    private final ServerConfig serverConfig;
+
+    private static final String FILE_DELIMETER = ",";
+
+
+    /**
+     * 通用上传请求(单个)
+     */
+    @PostMapping("/upload")
+    public R<Map<String,String>> uploadFile(MultipartFile file) throws Exception
+    {
+        try
+        {
+            String originalFilename = file.getOriginalFilename();
+            String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
+            //压缩图片 "bmp", "gif", "jpg", "jpeg", "png",
+            if(".png".equalsIgnoreCase(fileExtension) || ".bmp".equalsIgnoreCase(fileExtension)  || ".gif".equalsIgnoreCase(fileExtension) || ".jpeg".equalsIgnoreCase(fileExtension)){
+                file = ImageSizeUtil.compressImg(file);
+            }
+
+            // 上传文件路径
+            String filePath = BoManConfig.getUploadPath();
+            // 上传并返回新文件名称
+            String fileName = FileUploadUtils.upload(filePath, file);
+            String url = serverConfig.getUrl() + fileName;
+            Map<String, String> map = new HashMap<>();
+            //服务器路径
+            String urlOnline = filePath + fileName.replace("/profile/upload", "");
+            map.put("url", url);
+            map.put("urlOnline", urlOnline);
+            map.put("fileName", fileName);
+            map.put("newFileName", FileUtils.getName(fileName));
+            map.put("originalFilename", file.getOriginalFilename());
+            return R.ok(map);
+        }
+        catch (Exception e)
+        {
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 通用上传请求(多个)
+     */
+    @PostMapping("/uploads")
+    public R<Map<String,String>> uploadFiles(List<MultipartFile> files) throws Exception
+    {
+        try
+        {
+            // 上传文件路径
+            String filePath = BoManConfig.getUploadPath();
+            List<String> urls = new ArrayList<String>();
+            List<String> fileNames = new ArrayList<String>();
+            List<String> newFileNames = new ArrayList<String>();
+            List<String> originalFilenames = new ArrayList<String>();
+            for (MultipartFile file : files)
+            {
+                // 上传并返回新文件名称
+                String fileName = FileUploadUtils.upload(filePath, file);
+                String url = serverConfig.getUrl() + fileName;
+                urls.add(url);
+                fileNames.add(fileName);
+                newFileNames.add(FileUtils.getName(fileName));
+                originalFilenames.add(file.getOriginalFilename());
+            }
+            Map<String, String> map = new HashMap<>();
+            map.put("urls", StringUtils.join(urls, FILE_DELIMETER));
+            map.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
+            map.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
+            map.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
+            return R.ok(map);
+        }
+        catch (Exception e)
+        {
+            return R.fail(e.getMessage());
+        }
+    }
+}

+ 33 - 3
ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java

@@ -3,6 +3,7 @@ package org.dromara.web.service;
 import cn.dev33.satoken.secure.BCrypt;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.config.BoManConfig;
 import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.domain.model.RegisterBody;
@@ -24,6 +25,8 @@ import org.dromara.system.mapper.SysUserMapper;
 import org.dromara.system.service.ISysUserService;
 import org.springframework.stereotype.Service;
 
+import static org.dromara.common.core.constant.Constants.TWO;
+
 /**
  * 注册校验方法
  *
@@ -41,9 +44,11 @@ public class SysRegisterService {
      * 注册
      */
     public void register(RegisterBody registerBody) {
+        //手机号、手机验证码注册
         String tenantId = registerBody.getTenantId();
-        String username = registerBody.getUsername();
-        String password = registerBody.getPassword();
+        String username = registerBody.getPhonenumber();
+        //String password = registerBody.getPassword();
+        String password = BoManConfig.getPasswordDeft();
         // 校验用户类型是否存在
         String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
 
@@ -52,12 +57,16 @@ public class SysRegisterService {
         if (captchaEnabled) {
             validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid());
         }
+        boolean smsEnable = captchaProperties.getSmsEnable();
+        if (smsEnable) {
+            validateSmsCode(tenantId, username, registerBody.getCode(),registerBody.getPhonenumber());
+        }
         SysUserBo sysUser = new SysUserBo();
         sysUser.setUserName(username);
         sysUser.setNickName(username);
         sysUser.setPassword(BCrypt.hashpw(password));
         sysUser.setUserType(userType);
-
+        sysUser.setPhonenumber(username);
         boolean exist = TenantHelper.dynamic(tenantId, () -> {
             return userMapper.exists(new LambdaQueryWrapper<SysUser>()
                 .eq(SysUser::getUserName, sysUser.getUserName()));
@@ -93,6 +102,27 @@ public class SysRegisterService {
         }
     }
 
+    /**
+     * 校验短信验证码
+     *
+     * @param username 用户名
+     * @param code     短信验证码
+     * @param uuid     唯一标识
+     */
+    public void validateSmsCode(String tenantId, String username, String code, String uuid) {
+        String verifyKey = GlobalConstants.SMS_CAPTCHA_CODE_KEY + TWO + ":" + StringUtils.blankToDefault(uuid, "");
+        String captcha = RedisUtils.getCacheObject(verifyKey);
+        RedisUtils.deleteObject(verifyKey);
+        if (captcha == null) {
+            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.sms.expire"));
+            throw new CaptchaExpireException();
+        }
+        if (!code.equalsIgnoreCase(captcha)) {
+            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.sms.error"));
+            throw new CaptchaException();
+        }
+    }
+
     /**
      * 记录登录信息
      *

+ 4 - 2
ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java

@@ -30,6 +30,8 @@ import org.dromara.web.service.IAuthStrategy;
 import org.dromara.web.service.SysLoginService;
 import org.springframework.stereotype.Service;
 
+import static org.dromara.common.core.constant.Constants.ONE;
+
 /**
  * 短信认证策略
  *
@@ -79,9 +81,9 @@ public class SmsAuthStrategy implements IAuthStrategy {
      * 校验短信验证码
      */
     private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
-        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
+        String code = RedisUtils.getCacheObject(GlobalConstants.SMS_CAPTCHA_CODE_KEY + ONE + ":" + phonenumber);
         if (StringUtils.isBlank(code)) {
-            loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.sms.expire"));
             throw new CaptchaExpireException();
         }
         return code.equals(smsCode);

+ 2 - 5
ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java

@@ -3,10 +3,7 @@ package org.dromara.web.service.impl;
 import cn.dev33.satoken.stp.SaLoginModel;
 import cn.dev33.satoken.stp.StpUtil;
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.http.HttpUtil;
-import cn.hutool.http.Method;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.zhyd.oauth.model.AuthResponse;
@@ -68,7 +65,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
             throw new ServiceException(response.getMsg());
         }
         AuthUser authUserData = response.getData();
-        if ("GITEE".equals(authUserData.getSource())) {
+/*        if ("GITEE".equals(authUserData.getSource())) {
             // 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
             HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
                     .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
@@ -76,7 +73,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
             HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
                     .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
                     .executeAsync();
-        }
+        }*/
 
         List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
         if (CollUtil.isEmpty(list)) {

+ 28 - 63
ruoyi-admin/src/main/resources/application-dev.yml

@@ -1,3 +1,9 @@
+# 项目相关配置
+boman:
+  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
+  profile: D:/ruoyi/uploadPath/wuye
+  passwordDeft: 123456
+
 --- # 监控中心配置
 spring.boot.admin.client:
   # 增加客户端开关
@@ -49,9 +55,9 @@ spring:
           driverClassName: com.mysql.cj.jdbc.Driver
           # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
           # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
-          url: jdbc:mysql://60.171.161.56:25143/wuye_guanli_v2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          url: jdbc:mysql://127.0.0.1:3306/wuye_guanli_v2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: root
-          password: Boman123
+          password: 123456
 #        # 从库数据源
 #        slave:
 #          lazy: true
@@ -80,17 +86,17 @@ spring:
 #          password: root
       hikari:
         # 最大连接池数量
-        maxPoolSize: 20
+        maxPoolSize: 10
         # 最小空闲线程数量
-        minIdle: 10
+        minIdle: 5
         # 配置获取连接等待超时的时间
         connectionTimeout: 30000
         # 校验超时时间
         validationTimeout: 5000
         # 空闲连接存活最大时间,默认10分钟
-        idleTimeout: 600000
+        idleTimeout: 60000
         # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
-        maxLifetime: 1800000
+        maxLifetime: 90000
         # 多久检查一次连接的活性
         keepaliveTime: 30000
 
@@ -174,24 +180,25 @@ sms:
       # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
       supplier: alibaba
       # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
-      access-key-id: 您的accessKey
+      access-key-id: LTAI5tNA2fcBJH6EWRH6Pxr6
       # 称为accessSecret有些称之为apiSecret
-      access-key-secret: 您的accessKeySecret
-      signature: 您的短信签名
-      sdk-app-id: 您的sdkAppId
-    config2:
-      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
-      supplier: tencent
-      access-key-id: 您的accessKey
-      access-key-secret: 您的accessKeySecret
-      signature: 您的短信签名
-      sdk-app-id: 您的sdkAppId
+      access-key-secret: 5WdaPEOvC3u9LC7pwy2DQ9pgmJvgUr
+      signature: 中新云
+#      阿里云不需要sdk-app-id
+      sdk-app-id:
+#    config2:
+#      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+#      supplier: tencent
+#      access-key-id: 您的accessKey
+#      access-key-secret: 您的accessKeySecret
+#      signature: 您的短信签名
+#      sdk-app-id: 您的sdkAppId
 
 
 --- # 三方授权
 justauth:
   # 前端外网访问地址
-  address: http://localhost:80
+  address: https://zhsq.qs163.cn
   type:
     maxkey:
       # maxkey 服务器地址
@@ -200,66 +207,24 @@ justauth:
       client-id: 876892492581044224
       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
       redirect-uri: ${justauth.address}/social-callback?source=maxkey
-    topiam:
-      # topiam 服务器地址
-      server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
-      client-id: 449c4*********937************759
-      client-secret: ac7***********1e0************28d
-      redirect-uri: ${justauth.address}/social-callback?source=topiam
-      scopes: [openid, email, phone, profile]
-    qq:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=qq
-      union-id: false
-    weibo:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=weibo
-    gitee:
-      client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
-      client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
-      redirect-uri: ${justauth.address}/social-callback?source=gitee
-    dingtalk:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=dingtalk
-    baidu:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=baidu
-    csdn:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=csdn
-    coding:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=coding
-      coding-group-name: xx
-    oschina:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=oschina
     alipay_wallet:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
       redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
       alipay-public-key: MIIB**************DAQAB
+#      微信
     wechat_open:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
       redirect-uri: ${justauth.address}/social-callback?source=wechat_open
+      #      小程序
     wechat_mp:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
       redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
+      #      企业微信
     wechat_enterprise:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
       redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
       agent-id: 1000002
-    gitlab:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=gitlab

+ 15 - 55
ruoyi-admin/src/main/resources/application-prod.yml

@@ -1,10 +1,15 @@
+# 项目相关配置
+boman:
+  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
+  profile: /home/ruoyi/uploadPath/wuye
+  passwordDeft: 123456
 --- # 临时文件存储位置 避免临时文件被系统清理报错
 spring.servlet.multipart.location: /ruoyi/server/temp
 
 --- # 监控中心配置
 spring.boot.admin.client:
   # 增加客户端开关
-  enabled: true
+  enabled: false
   url: http://localhost:9090/admin
   instance:
     service-host-type: IP
@@ -16,7 +21,7 @@ spring.boot.admin.client:
 
 --- # snail-job 配置
 snail-job:
-  enabled: true
+  enabled: false
   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
   group: "ruoyi_group"
   # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
@@ -166,7 +171,7 @@ sms:
   # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
   restricted: true
   # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
-  minute-max: 1
+  minute-max: 5
   # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
   account-max: 30
   # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
@@ -182,13 +187,13 @@ sms:
       access-key-secret: 您的accessKeySecret
       signature: 您的短信签名
       sdk-app-id: 您的sdkAppId
-    config2:
-      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
-      supplier: tencent
-      access-key-id: 您的accessKey
-      access-key-secret: 您的accessKeySecret
-      signature: 您的短信签名
-      sdk-app-id: 您的sdkAppId
+#    config2:
+#      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+#      supplier: tencent
+#      access-key-id: 您的accessKey
+#      access-key-secret: 您的accessKeySecret
+#      signature: 您的短信签名
+#      sdk-app-id: 您的sdkAppId
 
 --- # 三方授权
 justauth:
@@ -202,47 +207,6 @@ justauth:
       client-id: 876892492581044224
       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
       redirect-uri: ${justauth.address}/social-callback?source=maxkey
-    topiam:
-      # topiam 服务器地址
-      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
-      client-id: 449c4*********937************759
-      client-secret: ac7***********1e0************28d
-      redirect-uri: ${justauth.address}/social-callback?source=topiam
-      scopes: [ openid, email, phone, profile ]
-    qq:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=qq
-      union-id: false
-    weibo:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=weibo
-    gitee:
-      client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
-      client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
-      redirect-uri: ${justauth.address}/social-callback?source=gitee
-    dingtalk:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=dingtalk
-    baidu:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=baidu
-    csdn:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=csdn
-    coding:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=coding
-      coding-group-name: xx
-    oschina:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=oschina
     alipay_wallet:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
@@ -261,7 +225,3 @@ justauth:
       client-secret: 1f7d08**********5b7**********29e
       redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
       agent-id: 1000002
-    gitlab:
-      client-id: 10**********6
-      client-secret: 1f7d08**********5b7**********29e
-      redirect-uri: ${justauth.address}/social-callback?source=gitlab

+ 3 - 1
ruoyi-admin/src/main/resources/application.yml

@@ -22,6 +22,7 @@ server:
 
 captcha:
   enable: false
+  smsEnable: true
   # 页面 <参数设置> 可开启关闭 验证码校验
   # 验证码类型 math 数组计算 char 字符验证
   type: MATH
@@ -127,6 +128,7 @@ tenant:
     - sys_user_role
     - sys_client
     - sys_oss_config
+    - sys_user_tenant
 
 # MyBatisPlus配置
 # https://baomidou.com/config/
@@ -163,7 +165,7 @@ mybatis-encryptor:
 # api接口加密
 api-decrypt:
   # 是否开启全局接口加密
-  enabled: true
+  enabled: false
   # AES 加密头标识
   headerFlag: encrypt-key
   # 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换

+ 64 - 61
ruoyi-admin/src/main/resources/i18n/messages.properties

@@ -1,61 +1,64 @@
-#错误消息
-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=未知错误,请重新登录
-auth.grant.type.error=认证权限类型错误
-auth.grant.type.blocked=认证权限类型已禁用
-auth.grant.type.not.blank=认证权限类型不能为空
-auth.clientid.not.blank=认证客户端id不能为空
-##文件上传消息
-upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{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]不能为空
-social.source.not.blank=第三方登录平台[source]不能为空
-social.code.not.blank=第三方登录平台[code]不能为空
-social.state.not.blank=第三方登录平台[state]不能为空
-##租户
-tenant.number.not.blank=租户编号不能为空
-tenant.not.exists=对不起, 您的租户不存在,请联系管理员
-tenant.blocked=对不起,您的租户已禁用,请联系管理员
-tenant.expired=对不起,您的租户已过期,请联系管理员
+#\u9519\u8BEF\u6D88\u606F
+not.null=* \u5FC5\u987B\u586B\u5199
+user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF
+user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548
+user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728.
+user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
+user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
+user.password.retry.limit.exceed=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
+user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664
+user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+user.logout.success=\u9000\u51FA\u6210\u529F
+length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
+user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
+user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934
+user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
+user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
+user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
+user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
+user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
+user.email.not.blank=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A
+user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
+user.login.success=\u767B\u5F55\u6210\u529F
+user.register.success=\u6CE8\u518C\u6210\u529F
+user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728
+user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458
+user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55
+user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
+user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
+auth.grant.type.error=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u9519\u8BEF
+auth.grant.type.blocked=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u5DF2\u7981\u7528
+auth.grant.type.not.blank=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+auth.clientid.not.blank=\u8BA4\u8BC1\u5BA2\u6237\u7AEFid\u4E0D\u80FD\u4E3A\u7A7A
+##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
+upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01
+upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26
+##\u6743\u9650
+no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
+repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
+rate.limiter.message=\u8BBF\u95EE\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
+sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
+sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
+email.code.not.blank=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
+email.code.retry.limit.count=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
+email.code.retry.limit.exceed=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
+xcx.code.not.blank=\u5C0F\u7A0B\u5E8F[code]\u4E0D\u80FD\u4E3A\u7A7A
+social.source.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[source]\u4E0D\u80FD\u4E3A\u7A7A
+social.code.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[code]\u4E0D\u80FD\u4E3A\u7A7A
+social.state.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[state]\u4E0D\u80FD\u4E3A\u7A7A
+##\u79DF\u6237
+tenant.number.not.blank=\u79DF\u6237\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+tenant.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u79DF\u6237\u4E0D\u5B58\u5728\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+tenant.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+tenant.expired=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+##\u77ED\u4FE1\u9A8C\u8BC1\u7801
+user.sms.error=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u9519\u8BEF
+user.sms.expire=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548

+ 16 - 14
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties

@@ -1,4 +1,4 @@
-#错误消息
+#\u9519\u8BEF\u6D88\u606F
 not.null=* Required fill in
 user.jcaptcha.error=Captcha error
 user.jcaptcha.expire=Captcha invalid
@@ -6,9 +6,9 @@ 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.password.delete=Sorry, your account\uFF1A{0} has been deleted
 user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
-role.blocked=Role disabledplease contact administrators
+role.blocked=Role disabled\uFF0Cplease 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
@@ -26,22 +26,22 @@ 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 exitplease login again
+user.forcelogout=The administrator is forced to exit\uFF0Cplease login again
 user.unknown.error=Unknown error, please login again
 auth.grant.type.error=Auth grant type error
 auth.grant.type.blocked=Auth grant type disabled
 auth.grant.type.not.blank=Auth grant type cannot be blank
 auth.clientid.not.blank=Auth clientid cannot be blank
-##文件上传消息
-upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is:{0}MB!
+##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
+upload.exceed.maxSize=The uploaded file size exceeds the limit file size\uFF01<br/>the maximum allowed file size is\uFF1A{0}MB\uFF01
 upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
-##权限
-no.permission=You do not have permission to the dataplease contact your administrator to add permissions [{0}]
-no.create.permission=You do not have permission to create dataplease contact your administrator to add permissions [{0}]
-no.update.permission=You do not have permission to modify dataplease contact your administrator to add permissions [{0}]
-no.delete.permission=You do not have permission to delete dataplease contact your administrator to add permissions [{0}]
-no.export.permission=You do not have permission to export dataplease contact your administrator to add permissions [{0}]
-no.view.permission=You do not have permission to view dataplease contact your administrator to add permissions [{0}]
+##\u6743\u9650
+no.permission=You do not have permission to the data\uFF0Cplease contact your administrator to add permissions [{0}]
+no.create.permission=You do not have permission to create data\uFF0Cplease contact your administrator to add permissions [{0}]
+no.update.permission=You do not have permission to modify data\uFF0Cplease contact your administrator to add permissions [{0}]
+no.delete.permission=You do not have permission to delete data\uFF0Cplease contact your administrator to add permissions [{0}]
+no.export.permission=You do not have permission to export data\uFF0Cplease contact your administrator to add permissions [{0}]
+no.view.permission=You do not have permission to view data\uFF0Cplease 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
@@ -54,8 +54,10 @@ xcx.code.not.blank=Mini program [code] cannot be blank
 social.source.not.blank=Social login platform [source] cannot be blank
 social.code.not.blank=Social login platform [code] cannot be blank
 social.state.not.blank=Social login platform [state] cannot be blank
-##租户
+##\u79DF\u6237
 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.
+user.sms.error=SmsCaptcha error
+user.sms.expire=SmsCaptcha invalid

+ 4 - 0
ruoyi-admin/src/main/resources/logback-plus.xml

@@ -1,5 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
+
+    <!-- 设置日志级别 -->
+    <logger name="com.baomidou.mybatisplus" level="DEBUG" />
+    <logger name="org.mybatis" level="DEBUG" />
     <property name="log.path" value="./logs"/>
     <property name="console.log.pattern"
               value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>

+ 28 - 0
ruoyi-common/ruoyi-common-core/pom.xml

@@ -16,6 +16,28 @@
     </description>
 
     <dependencies>
+
+        <!--thumbnailator 压缩工具-->
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.8</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <!--springboot3弃用-->
+<!--        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.5</version>
+        </dependency>-->
         <!-- Spring框架基本的核心工具 -->
         <dependency>
             <groupId>org.springframework</groupId>
@@ -93,6 +115,12 @@
             <groupId>org.lionsoul</groupId>
             <artifactId>ip2region</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+        </dependency>
+
+
 
     </dependencies>
 

+ 62 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/BoManConfig.java

@@ -0,0 +1,62 @@
+package org.dromara.common.core.config;
+
+import org.dromara.common.core.config.properties.BoManProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author: tjf
+ * @Date: 2025/4/8 17:44
+ * @Describe:
+ */
+@Component
+public class BoManConfig {
+    private static BoManProperties boManProperties;
+
+    @Autowired
+    public BoManConfig(BoManProperties boManProperties) {
+        this.boManProperties = boManProperties;
+    }
+
+    /**
+     * 获取导入上传路径
+     */
+    public static String getImportPath() {
+        return boManProperties.getProfile() + "/import";
+    }
+
+    /**
+     * 获取头像上传路径
+     */
+    public static String getAvatarPath() {
+        return boManProperties.getProfile() + "/avatar";
+    }
+
+    /**
+     * 获取下载路径
+     */
+    public static String getDownloadPath() {
+        return boManProperties.getProfile() + "/download/";
+    }
+
+    /**
+     * 获取上传路径
+     */
+    public static String getUploadPath() {
+        return boManProperties.getProfile() + "/upload";
+    }
+
+    /**
+     * 获取原始上传路径
+     */
+    public static String getProfile() {
+        return boManProperties.getProfile();
+    }
+
+    /**
+     * 获取用手机号,短信验证码注册账号的默认密码
+     */
+    public static String getPasswordDeft() {
+        return boManProperties.getPasswordDeft();
+    }
+}

+ 35 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ServerConfig.java

@@ -0,0 +1,35 @@
+package org.dromara.common.core.config;
+
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.dromara.common.core.utils.ServletUtils;
+import org.springframework.stereotype.Component;
+
+
+
+/**
+ * 服务相关配置
+ *
+ * @author ruoyi
+ */
+@Component
+public class ServerConfig
+{
+    /**
+     * 获取完整的请求路径,包括:域名,端口,上下文访问路径
+     *
+     * @return 服务地址
+     */
+    public String getUrl()
+    {
+        HttpServletRequest request = ServletUtils.getRequest();
+        return getDomain(request);
+    }
+
+    public static String getDomain(HttpServletRequest request)
+    {
+        StringBuffer url = request.getRequestURL();
+        String contextPath = request.getServletContext().getContextPath();
+        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
+    }
+}

+ 27 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/properties/BoManProperties.java

@@ -0,0 +1,27 @@
+package org.dromara.common.core.config.properties;
+
+import lombok.Data;
+import lombok.Getter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author: tjf
+ * @Date: 2025/4/9 10:04
+ * @Describe:
+ */
+@Data
+@ConfigurationProperties(prefix = "boman")
+@Component
+@Getter
+public class BoManProperties {
+    /**
+     * 上传路径
+     */
+    private String profile;
+
+    /**
+     * 用手机号,短信验证码注册账号的默认密码
+     */
+    private String passwordDeft;
+}

+ 81 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/Constants.java

@@ -65,12 +65,92 @@ public interface Constants {
     /**
      * 验证码有效期(分钟)
      */
-    Integer CAPTCHA_EXPIRATION = 2;
+    Integer CAPTCHA_EXPIRATION = 5;
 
     /**
      * 顶级父级id
      */
     Long TOP_PARENT_ID = 0L;
 
+    /**
+     * 资源映射路径 前缀
+     */
+    String RESOURCE_PREFIX = "/profile";
+
+    String ONE = "1";
+    String TWO = "2";
+    String THR = "3";
+    String FOR = "4";
+    String FIV = "5";
+    String Y = "Y";
+    String N = "N";
+    String FRONT = "front";
+    String BACK = "back";
+    String UP = "up";
+    String DOWN = "down";
+    //持平
+    String FLAT = "flat";
+    //-----------------------web首页统计的key--------------
+//web首页统计上月外来车辆数量
+    String LAST_MONTH_VISITOR_CAR = "last_month_visitor_car:";
+    //web首页统计当月外来车辆数量
+    String NOW_MONTH_VISITOR_CAR = "now_month_visitor_car:";
+    //web首页统计上月违停车辆数量
+    String LAST_MONTH_ILLEGAL_CAR = "last_month_illegal_car:";
+    //web首页统计当月违停车辆数量
+    String NOW_MONTH_ILLEGAL_CAR = "now_month_illegal_car:";
+    //--------------------app首页统计的key----------------
+//用户个人报修维修未完成的数量property_repair_no:{userId} KEY=数量
+    String PROPERTY_REPAIR_NO = "property_repair_no:";
+    //维修未完成的总数量 property_repair_no_all: KEY=数量
+    String PROPERTY_REPAIR_NO_ALL = "property_repair_no_all:";
+    //用户个人投诉建议未回复的数量 complaint_suggestion_no:{userId} KEY=数量
+    String COMPLAINT_SUGGESTION_NO = "complaint_suggestion_no:";
+    //投诉建议未回复的总数量
+    String COMPLAINT_SUGGESTION_NO_ALL = "complaint_suggestion_no_all:";
+
+
+    //------------------文章的key----------------------------
+//社区资讯文章点赞的人员集合的key value = 该文章的[userId]
+    String ONE_LIKE = "one_like:";
+    //对应用户点赞的文章key=my_like_one:{用户id} value =[资讯文章id]
+    String ONE_MY_LIKE = "one_my_like:";
+    //社区资讯文章点赞的数量 给数据库同步数据在 redis中增加一个有过期时间的key key=one_like_count_time:{文章id}#{数量}  过期时候value获取不到
+    String ONE_LIKE_COUNT_TIME = "one_like_count_time:";
+    //社区资讯文章点赞的数量永久key key=like_one_count:{文章id} value = 该文章的点赞数量
+    String ONE_LIKE_COUNT = "one_like_count:";
+
+    //------------------社区资讯key----------------------------
+//社区资讯评论的总数量 key=community_comment_count:{communityId} value = 评论的数量
+    String COMMUNITY_COMMENT_COUNT = "community_comment_count:";
+    //------------------回复的key----------------------------
+//社区资讯回复点赞的人员集合的key value = 该回复的[userId]
+    String TWO_LIKE = "two_like:";
+    //对应用户点赞的回复key=two_my_like:{用户id} value = [评论id]
+    String TWO_MY_LIKE = "two_my_like:";
+    //社区资讯评论点赞的数量 给数据库同步数据在 redis中增加一个有过期时间的key key=two_like_count_time:{评论id}#{数量} 过期时候value获取不到
+    String TWO_LIKE_COUNT_TIME = "two_like_count_time:";
+    //社区资讯评论点赞的数量永久key key=two_like_count:{评论id} value = 该评论的点赞数量
+    String TWO_LIKE_COUNT = "two_like_count:";
+
+    //-------------------社区资讯收藏的key-----------------
+//社区资讯收藏的人员集合的key = stars_community:{communityId} value = 该资讯收藏的[userId]
+    String STARS_COMMUNITY = "stars_community:";
+    //社区资讯收藏的总数的key = stars_user_count:{communityId} value = 收藏的人数值
+    String STARS_USER_COUNT = "stars_user_count:";
+
+    //-------------------党建资讯收藏的key-----------------
+//党建资讯收藏的人员集合的key = stars_party_news:{userId} value = 该用户收藏的党建[partyId]
+    String STARS_PARTY_NEWS = "stars_party_news:";
+    //党建资讯对应资讯收藏的数量key =stars_party_news_count:{partyId} value = 收藏的数量
+    String STARS_PARTY_NEWS_COUNT = "stars_party_news_count:";
+
+    //-------------------未读互动的key-----------------
+//记录社区资讯某个用户一共有多少个未读的互动(点赞,收藏,回复)的具体信息,记录成map形式{} key = comment_interaction_user:{userId} hkey = {targetType:targetId} value= CommentInteractionVo
+    String COMMENT_INTERACTION_USER = "comment_interaction_user:";
+    //记录社区资讯某个用户一共有多少个未读的互动的数量key = comment_interaction_user_count:{userId} value = 未读的互动的数量
+    String COMMENT_INTERACTION_USER_COUNT = "comment_interaction_user_count:";
+    //记录社区资讯某个用户对应资讯下有多少个未读的互动key=comment_interaction_community_user_count:{userId} hkey = {community_id} value = 资讯下有多少个未读的互动(点赞,收藏,回复)
+    String COMMENT_INTERACTION_COMMUNITY_USER_COUNT = "comment_interaction_community_user_count:";
 }
 

+ 4 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java

@@ -16,6 +16,10 @@ public interface GlobalConstants {
      * 验证码 redis key
      */
     String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
+    /**
+     * 短信验证码
+     */
+    String SMS_CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "sms_captcha_codes:";
 
     /**
      * 防重提交 redis key

+ 11 - 5
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java

@@ -1,9 +1,9 @@
 package org.dromara.common.core.domain.model;
 
 import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import org.hibernate.validator.constraints.Length;
 
 /**
  * 用户注册对象
@@ -17,17 +17,23 @@ public class RegisterBody extends LoginBody {
     /**
      * 用户名
      */
-    @NotBlank(message = "{user.username.not.blank}")
-    @Length(min = 2, max = 20, message = "{user.username.length.valid}")
+    //@NotBlank(message = "{user.username.not.blank}")
+    //@Length(min = 2, max = 20, message = "{user.username.length.valid}")
     private String username;
 
     /**
      * 用户密码
      */
-    @NotBlank(message = "{user.password.not.blank}")
-    @Length(min = 5, max = 20, message = "{user.password.length.valid}")
+    //@NotBlank(message = "{user.password.not.blank}")
+    //@Length(min = 5, max = 20, message = "{user.password.length.valid}")
     private String password;
 
     private String userType;
+    /**
+     * 手机号
+     */
+    @NotBlank(message = "手机号不能为空")
+    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
+    private String phonenumber;
 
 }

+ 6 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java

@@ -14,10 +14,15 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum UserType {
 
+
     /**
-     * pc端
+     *系统用户
      */
     SYS_USER("sys_user"),
+    /**
+     * pc端
+     */
+    PC_USER("pc_user"),
 
     /**
      * app端

+ 26 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/UtilException.java

@@ -0,0 +1,26 @@
+package org.dromara.common.core.exception;
+
+/**
+ * 工具类异常
+ *
+ * @author ruoyi
+ */
+public class UtilException extends RuntimeException
+{
+    private static final long serialVersionUID = 8247610319171014183L;
+
+    public UtilException(Throwable e)
+    {
+        super(e.getMessage(), e);
+    }
+
+    public UtilException(String message)
+    {
+        super(message);
+    }
+
+    public UtilException(String message, Throwable throwable)
+    {
+        super(message, throwable);
+    }
+}

+ 61 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileUploadException.java

@@ -0,0 +1,61 @@
+package org.dromara.common.core.exception.file;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * 文件上传异常类
+ *
+ * @author ruoyi
+ */
+public class FileUploadException extends Exception
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private final Throwable cause;
+
+    public FileUploadException()
+    {
+        this(null, null);
+    }
+
+    public FileUploadException(final String msg)
+    {
+        this(msg, null);
+    }
+
+    public FileUploadException(String msg, Throwable cause)
+    {
+        super(msg);
+        this.cause = cause;
+    }
+
+    @Override
+    public void printStackTrace(PrintStream stream)
+    {
+        super.printStackTrace(stream);
+        if (cause != null)
+        {
+            stream.println("Caused by:");
+            cause.printStackTrace(stream);
+        }
+    }
+
+    @Override
+    public void printStackTrace(PrintWriter writer)
+    {
+        super.printStackTrace(writer);
+        if (cause != null)
+        {
+            writer.println("Caused by:");
+            cause.printStackTrace(writer);
+        }
+    }
+
+    @Override
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}

+ 81 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/InvalidExtensionException.java

@@ -0,0 +1,81 @@
+package org.dromara.common.core.exception.file;
+
+
+import java.util.Arrays;
+
+/**
+ * 文件上传 误异常类
+ *
+ * @author ruoyi
+ */
+public class InvalidExtensionException extends FileUploadException
+{
+    private static final long serialVersionUID = 1L;
+
+    private String[] allowedExtension;
+    private String extension;
+    private String filename;
+
+    public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
+    {
+        super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式");
+        this.allowedExtension = allowedExtension;
+        this.extension = extension;
+        this.filename = filename;
+    }
+
+    public String[] getAllowedExtension()
+    {
+        return allowedExtension;
+    }
+
+    public String getExtension()
+    {
+        return extension;
+    }
+
+    public String getFilename()
+    {
+        return filename;
+    }
+
+    public static class InvalidImageExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidFlashExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidMediaExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidVideoExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+}

+ 103 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ImageSizeUtil.java

@@ -0,0 +1,103 @@
+package org.dromara.common.core.utils;
+
+import net.coobird.thumbnailator.Thumbnails;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.multipart.MultipartFile;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 获取图片尺寸和截图 工具类
+ *
+ * @author zhangxiaoyan
+ */
+public class ImageSizeUtil {
+    /**
+     * 压缩图片 并返回MultipartFile
+     * @param multiFile
+     * @return
+     * @throws Exception
+     */
+    public static MultipartFile compressImg(MultipartFile multiFile) {
+        long size = multiFile.getSize();
+        if (size> 0){
+            double fileSizeMb = size / (1024.0 * 1024.0);
+            if (fileSizeMb < 5){
+                return multiFile;
+            }
+        }
+        // 压缩图片
+        InputStream inputStream = null;
+        ByteArrayOutputStream bos = null;
+        MultipartFile multipartFile = null;
+        InputStream fileInput= null;
+        try {
+            inputStream = multiFile.getInputStream();
+            bos = new ByteArrayOutputStream();
+            // 压缩图片核心代码 scale设置压缩比例 越小压缩图片越小
+            Thumbnails.of(inputStream).scale(0.5f).outputQuality(1f).toOutputStream(bos);
+            fileInput = new ByteArrayInputStream(bos.toByteArray());
+            // 转换 MultipartFile
+            String fieldName = multiFile.getName();
+            String fileName = multiFile.getOriginalFilename();
+            String contentType = multiFile.getContentType();
+            /**
+             * filename:文件名
+             * file.getName():原文件名称
+             * file.getContentType():原文件ContentType
+             * fileStream:文件输入字节流
+             */
+            multipartFile = new MockMultipartFile(fieldName, fileName, contentType, fileInput);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (inputStream != null){
+                    inputStream.close();
+                }
+                if (bos != null){
+                    bos.close();
+                }
+                if (fileInput != null){
+                    fileInput.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+
+            }
+        }
+        return multipartFile;
+    }
+
+/*    public static MultipartFile getMulFileByFile(InputStream fis,String fieldName,String contentType,String fileName) {
+        FileItem fileItem = createFileItem(fis,fieldName,contentType,fileName);
+        MultipartFile mfile = new CommonsMultipartFile(fileItem);
+        return mfile;
+    }
+
+    public static FileItem createFileItem(InputStream fis,String fieldName,String contentType,String fileName){
+        FileItemFactory factory = new DiskFileItemFactory(16, null);
+        FileItem item = factory.createItem(fieldName, contentType, false,fileName);
+        int bytesRead = 0;
+        byte[] buffer = new byte[8192];
+        try
+        {
+            OutputStream os = item.getOutputStream();
+            while ((bytesRead = fis.read(buffer, 0, 8192))!= -1)
+            {
+                os.write(buffer, 0, bytesRead);
+            }
+            os.close();
+            fis.close();
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+        return item;
+    }*/
+}
+
+

+ 111 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/PwdCheckUtil.java

@@ -0,0 +1,111 @@
+package org.dromara.common.core.utils;
+
+/**
+ * @Author: tjf
+ * @Date: 2022/10/10 9:24
+ * @Describe:
+ */
+public class PwdCheckUtil {
+    //定义特殊字符
+    public static String SPECIAL_CHAR = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
+
+    /**
+     * @brief   检测密码中字符长度
+     * @param[in] password            密码字符串
+     * @return  符合长度要求 返回true
+     */
+    public static boolean checkPasswordLength(String password, String minNum, String maxNum) {
+        boolean flag =false;
+        if (StringUtils.isBlank(maxNum))  {
+            minNum = StringUtils.isBlank(minNum) ? "0":minNum;
+            if (password.length() >= Integer.parseInt(minNum)) {
+                flag = true;
+            }
+        } else {
+            minNum = StringUtils.isBlank(minNum) ? "0":minNum;
+            if (password.length() >= Integer.parseInt(minNum) &&
+                    password.length() <= Integer.parseInt(maxNum)) {
+                flag = true;
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * @brief   检测密码中是否包含数字
+     * @param[in] password            密码字符串
+     * @return  包含数字 返回true
+     */
+    public static boolean checkContainDigit(String password) {
+        char[] chPass = password.toCharArray();
+        for (int i = 0; i < chPass.length; i++) {
+            if (Character.isDigit(chPass[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @brief   检测密码中是否包含字母(不区分大小写)
+     * @param[in] password            密码字符串
+     * @return  包含字母 返回true
+     */
+    public static boolean checkContainCase(String password) {
+        char[] chPass = password.toCharArray();
+        for (int i = 0; i < chPass.length; i++) {
+            if (Character.isLetter(chPass[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * @brief   检测密码中是否包含小写字母
+     * @param[in] password            密码字符串
+     * @return  包含小写字母 返回true
+     */
+    public static boolean checkContainLowerCase(String password) {
+        char[] chPass = password.toCharArray();
+        for (int i = 0; i < chPass.length; i++) {
+            if (Character.isLowerCase(chPass[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * @brief   检测密码中是否包含大写字母
+     * @param[in] password            密码字符串
+     * @return  包含大写字母 返回true
+     */
+    public static boolean checkContainUpperCase(String password) {
+        char[] chPass = password.toCharArray();
+        for (int i = 0; i < chPass.length; i++) {
+            if (Character.isUpperCase(chPass[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * @brief   检测密码中是否包含特殊符号
+     * @param[in] password            密码字符串
+     * @return  包含特殊符号 返回true
+     */
+    public static boolean checkContainSpecialChar(String password) {
+        char[] chPass = password.toCharArray();
+        for (int i = 0; i < chPass.length; i++) {
+            if (SPECIAL_CHAR.indexOf(chPass[i]) != -1) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 234 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/FileUploadUtils.java

@@ -0,0 +1,234 @@
+package org.dromara.common.core.utils.file;
+
+
+import org.apache.commons.io.FilenameUtils;
+import org.dromara.common.core.config.BoManConfig;
+import org.dromara.common.core.constant.Constants;
+import org.dromara.common.core.exception.file.FileNameLengthLimitExceededException;
+import org.dromara.common.core.exception.file.FileSizeLimitExceededException;
+import org.dromara.common.core.exception.file.InvalidExtensionException;
+import org.dromara.common.core.utils.DateUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.uuid.Seq;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+/**
+ * 文件上传工具类
+ *
+ * @author ruoyi
+ */
+public class FileUploadUtils
+{
+    /**
+     * 默认大小 50M
+     */
+    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
+
+    /**
+     * 默认的文件名最大长度 100
+     */
+    public static final int DEFAULT_FILE_NAME_LENGTH = 100;
+
+    /**
+     * 默认上传的地址
+     */
+    private static String defaultBaseDir = BoManConfig.getProfile();
+
+    public static void setDefaultBaseDir(String defaultBaseDir)
+    {
+        FileUploadUtils.defaultBaseDir = defaultBaseDir;
+    }
+
+    public static String getDefaultBaseDir()
+    {
+        return defaultBaseDir;
+    }
+
+    /**
+     * 以默认配置进行文件上传
+     *
+     * @param file 上传的文件
+     * @return 文件名称
+     * @throws Exception
+     */
+    public static final String upload(MultipartFile file) throws IOException
+    {
+        try
+        {
+            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 根据文件路径上传
+     *
+     * @param baseDir 相对应用的基目录
+     * @param file 上传的文件
+     * @return 文件名称
+     * @throws IOException
+     */
+    public static final String upload(String baseDir, MultipartFile file) throws IOException
+    {
+        try
+        {
+            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 文件上传
+     *
+     * @param baseDir 相对应用的基目录
+     * @param file 上传的文件
+     * @param allowedExtension 上传文件类型
+     * @return 返回上传成功的文件名
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws FileNameLengthLimitExceededException 文件名太长
+     * @throws IOException 比如读写文件出错时
+     * @throws InvalidExtensionException 文件校验异常
+     */
+    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+        InvalidExtensionException
+    {
+        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
+        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
+        {
+            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+        }
+
+        assertAllowed(file, allowedExtension);
+
+        String fileName = extractFilename(file);
+
+        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
+        file.transferTo(Paths.get(absPath));
+        return getPathFileName(baseDir, fileName);
+    }
+
+    /**
+     * 编码文件名
+     */
+    public static final String extractFilename(MultipartFile file)
+    {
+        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
+                FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
+    }
+
+    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
+    {
+        File desc = new File(uploadDir + File.separator + fileName);
+
+        if (!desc.exists())
+        {
+            if (!desc.getParentFile().exists())
+            {
+                desc.getParentFile().mkdirs();
+            }
+        }
+        return desc;
+    }
+
+    public static final String getPathFileName(String uploadDir, String fileName) throws IOException
+    {
+        int dirLastIndex = BoManConfig.getProfile().length() + 1;
+        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
+        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
+    }
+
+    /**
+     * 文件大小校验
+     *
+     * @param file 上传的文件
+     * @return
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws InvalidExtensionException
+     */
+    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, InvalidExtensionException
+    {
+        long size = file.getSize();
+        if (size > DEFAULT_MAX_SIZE)
+        {
+            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
+        }
+
+        String fileName = file.getOriginalFilename();
+        String extension = getExtension(file);
+        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
+        {
+            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else
+            {
+                throw new InvalidExtensionException(allowedExtension, extension, fileName);
+            }
+        }
+    }
+
+    /**
+     * 判断MIME类型是否是允许的MIME类型
+     *
+     * @param extension
+     * @param allowedExtension
+     * @return
+     */
+    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
+    {
+        for (String str : allowedExtension)
+        {
+            if (str.equalsIgnoreCase(extension))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取文件名的后缀
+     *
+     * @param file 表单文件
+     * @return 后缀名
+     */
+    public static final String getExtension(MultipartFile file)
+    {
+        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+        if (StringUtils.isEmpty(extension))
+        {
+            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
+        }
+        return extension;
+    }
+}

+ 11 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java

@@ -36,5 +36,15 @@ public class MimeTypeUtils {
         "mp4", "avi", "rmvb",
         // pdf
         "pdf"};
-
+    public static String getExtension(String prefix)
+    {
+        return switch (prefix) {
+            case IMAGE_PNG -> "png";
+            case IMAGE_JPG -> "jpg";
+            case IMAGE_JPEG -> "jpeg";
+            case IMAGE_BMP -> "bmp";
+            case IMAGE_GIF -> "gif";
+            default -> "";
+        };
+    }
 }

+ 49 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/uuid/IdUtils.java

@@ -0,0 +1,49 @@
+package org.dromara.common.core.utils.uuid;
+
+/**
+ * ID生成器工具类
+ *
+ * @author ruoyi
+ */
+public class IdUtils
+{
+    /**
+     * 获取随机UUID
+     *
+     * @return 随机UUID
+     */
+    public static String randomUUID()
+    {
+        return UUID.randomUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线
+     *
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String simpleUUID()
+    {
+        return UUID.randomUUID().toString(true);
+    }
+
+    /**
+     * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
+     *
+     * @return 随机UUID
+     */
+    public static String fastUUID()
+    {
+        return UUID.fastUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
+     *
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String fastSimpleUUID()
+    {
+        return UUID.fastUUID().toString(true);
+    }
+}

+ 88 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/uuid/Seq.java

@@ -0,0 +1,88 @@
+package org.dromara.common.core.utils.uuid;
+
+
+import org.dromara.common.core.utils.DateUtils;
+import org.dromara.common.core.utils.StringUtils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author ruoyi 序列生成类
+ */
+public class Seq
+{
+    // 通用序列类型
+    public static final String commSeqType = "COMMON";
+
+    // 上传序列类型
+    public static final String uploadSeqType = "UPLOAD";
+
+    // 通用接口序列数
+    private static AtomicInteger commSeq = new AtomicInteger(1);
+
+    // 上传接口序列数
+    private static AtomicInteger uploadSeq = new AtomicInteger(1);
+
+    // 机器标识
+    private static final String machineCode = "A";
+
+    /**
+     * 获取通用序列号
+     *
+     * @return 序列值
+     */
+    public static String getId()
+    {
+        return getId(commSeqType);
+    }
+
+    /**
+     * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
+     *
+     * @return 序列值
+     */
+    public static String getId(String type)
+    {
+        AtomicInteger atomicInt = commSeq;
+        if (uploadSeqType.equals(type))
+        {
+            atomicInt = uploadSeq;
+        }
+        return getId(atomicInt, 3);
+    }
+
+    /**
+     * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
+     *
+     * @param atomicInt 序列数
+     * @param length 数值长度
+     * @return 序列值
+     */
+    public static String getId(AtomicInteger atomicInt, int length)
+    {
+        String result = DateUtils.dateTimeNow();
+        result += machineCode;
+        result += getSeq(atomicInt, length);
+        return result;
+    }
+
+    /**
+     * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
+     *
+     * @return 序列值
+     */
+    private synchronized static String getSeq(AtomicInteger atomicInt, int length)
+    {
+        // 先取值再+1
+        int value = atomicInt.getAndIncrement();
+
+        // 如果更新后值>=10 的 (length)幂次方则重置为1
+        int maxSeq = (int) Math.pow(10, length);
+        if (atomicInt.get() >= maxSeq)
+        {
+            atomicInt.set(1);
+        }
+        // 转字符串,用0左补齐
+        return StringUtils.padl(value, length);
+    }
+}

+ 486 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/uuid/UUID.java

@@ -0,0 +1,486 @@
+package org.dromara.common.core.utils.uuid;
+
+
+import org.dromara.common.core.exception.UtilException;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 提供通用唯一识别码(universally unique identifier)(UUID)实现
+ *
+ * @author ruoyi
+ */
+public final class UUID implements java.io.Serializable, Comparable<UUID>
+{
+    private static final long serialVersionUID = -1185015143654744140L;
+
+    /**
+     * SecureRandom 的单例
+     *
+     */
+    private static class Holder
+    {
+        static final SecureRandom numberGenerator = getSecureRandom();
+    }
+
+    /** 此UUID的最高64有效位 */
+    private final long mostSigBits;
+
+    /** 此UUID的最低64有效位 */
+    private final long leastSigBits;
+
+    /**
+     * 私有构造
+     *
+     * @param data 数据
+     */
+    private UUID(byte[] data)
+    {
+        long msb = 0;
+        long lsb = 0;
+        assert data.length == 16 : "data must be 16 bytes in length";
+        for (int i = 0; i < 8; i++)
+        {
+            msb = (msb << 8) | (data[i] & 0xff);
+        }
+        for (int i = 8; i < 16; i++)
+        {
+            lsb = (lsb << 8) | (data[i] & 0xff);
+        }
+        this.mostSigBits = msb;
+        this.leastSigBits = lsb;
+    }
+
+    /**
+     * 使用指定的数据构造新的 UUID。
+     *
+     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
+     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
+     */
+    public UUID(long mostSigBits, long leastSigBits)
+    {
+        this.mostSigBits = mostSigBits;
+        this.leastSigBits = leastSigBits;
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID fastUUID()
+    {
+        return randomUUID(false);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID()
+    {
+        return randomUUID(true);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID(boolean isSecure)
+    {
+        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
+
+        byte[] randomBytes = new byte[16];
+        ng.nextBytes(randomBytes);
+        randomBytes[6] &= 0x0f; /* clear version */
+        randomBytes[6] |= 0x40; /* set to version 4 */
+        randomBytes[8] &= 0x3f; /* clear variant */
+        randomBytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(randomBytes);
+    }
+
+    /**
+     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
+     *
+     * @param name 用于构造 UUID 的字节数组。
+     *
+     * @return 根据指定数组生成的 {@code UUID}
+     */
+    public static UUID nameUUIDFromBytes(byte[] name)
+    {
+        MessageDigest md;
+        try
+        {
+            md = MessageDigest.getInstance("MD5");
+        }
+        catch (NoSuchAlgorithmException nsae)
+        {
+            throw new InternalError("MD5 not supported");
+        }
+        byte[] md5Bytes = md.digest(name);
+        md5Bytes[6] &= 0x0f; /* clear version */
+        md5Bytes[6] |= 0x30; /* set to version 3 */
+        md5Bytes[8] &= 0x3f; /* clear variant */
+        md5Bytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(md5Bytes);
+    }
+
+    /**
+     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
+     *
+     * @param name 指定 {@code UUID} 字符串
+     * @return 具有指定值的 {@code UUID}
+     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
+     *
+     */
+    public static UUID fromString(String name)
+    {
+        String[] components = name.split("-");
+        if (components.length != 5)
+        {
+            throw new IllegalArgumentException("Invalid UUID string: " + name);
+        }
+        for (int i = 0; i < 5; i++)
+        {
+            components[i] = "0x" + components[i];
+        }
+
+        long mostSigBits = Long.decode(components[0]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[2]).longValue();
+
+        long leastSigBits = Long.decode(components[3]).longValue();
+        leastSigBits <<= 48;
+        leastSigBits |= Long.decode(components[4]).longValue();
+
+        return new UUID(mostSigBits, leastSigBits);
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最低有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中的最低有效 64 位。
+     */
+    public long getLeastSignificantBits()
+    {
+        return leastSigBits;
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最高有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中最高有效 64 位。
+     */
+    public long getMostSignificantBits()
+    {
+        return mostSigBits;
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
+     * <p>
+     * 版本号具有以下含意:
+     * <ul>
+     * <li>1 基于时间的 UUID
+     * <li>2 DCE 安全 UUID
+     * <li>3 基于名称的 UUID
+     * <li>4 随机生成的 UUID
+     * </ul>
+     *
+     * @return 此 {@code UUID} 的版本号
+     */
+    public int version()
+    {
+        // Version is bits masked by 0x000000000000F000 in MS long
+        return (int) ((mostSigBits >> 12) & 0x0f);
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
+     * <p>
+     * 变体号具有以下含意:
+     * <ul>
+     * <li>0 为 NCS 向后兼容保留
+     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
+     * <li>6 保留,微软向后兼容
+     * <li>7 保留供以后定义使用
+     * </ul>
+     *
+     * @return 此 {@code UUID} 相关联的变体号
+     */
+    public int variant()
+    {
+        // This field is composed of a varying number of bits.
+        // 0 - - Reserved for NCS backward compatibility
+        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
+        // 1 1 0 Reserved, Microsoft backward compatibility
+        // 1 1 1 Reserved for future definition.
+        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
+    }
+
+    /**
+     * 与此 UUID 相关联的时间戳值。
+     *
+     * <p>
+     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
+     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
+     *
+     * <p>
+     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
+     */
+    public long timestamp() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (mostSigBits & 0x0FFFL) << 48//
+                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
+                | mostSigBits >>> 32;
+    }
+
+    /**
+     * 与此 UUID 相关联的时钟序列值。
+     *
+     * <p>
+     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
+     * <p>
+     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
+     * UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的时钟序列
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public int clockSequence() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
+    }
+
+    /**
+     * 与此 UUID 相关的节点值。
+     *
+     * <p>
+     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
+     * <p>
+     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的节点值
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public long node() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return leastSigBits & 0x0000FFFFFFFFFFFFL;
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @return 此{@code UUID} 的字符串表现形式
+     * @see #toString(boolean)
+     */
+    @Override
+    public String toString()
+    {
+        return toString(false);
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
+     * @return 此{@code UUID} 的字符串表现形式
+     */
+    public String toString(boolean isSimple)
+    {
+        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
+        // time_low
+        builder.append(digits(mostSigBits >> 32, 8));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_mid
+        builder.append(digits(mostSigBits >> 16, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_high_and_version
+        builder.append(digits(mostSigBits, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // variant_and_sequence
+        builder.append(digits(leastSigBits >> 48, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // node
+        builder.append(digits(leastSigBits, 12));
+
+        return builder.toString();
+    }
+
+    /**
+     * 返回此 UUID 的哈希码。
+     *
+     * @return UUID 的哈希码值。
+     */
+    @Override
+    public int hashCode()
+    {
+        long hilo = mostSigBits ^ leastSigBits;
+        return ((int) (hilo >> 32)) ^ (int) hilo;
+    }
+
+    /**
+     * 将此对象与指定对象比较。
+     * <p>
+     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
+     *
+     * @param obj 要与之比较的对象
+     *
+     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+        if ((null == obj) || (obj.getClass() != UUID.class))
+        {
+            return false;
+        }
+        UUID id = (UUID) obj;
+        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
+    }
+
+    // Comparison Operations
+
+    /**
+     * 将此 UUID 与指定的 UUID 比较。
+     *
+     * <p>
+     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
+     *
+     * @param val 与此 UUID 比较的 UUID
+     *
+     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
+     *
+     */
+    @Override
+    public int compareTo(UUID val)
+    {
+        // The ordering is intentionally set up so that the UUIDs
+        // can simply be numerically compared as two numbers
+        return (this.mostSigBits < val.mostSigBits ? -1 : //
+                (this.mostSigBits > val.mostSigBits ? 1 : //
+                        (this.leastSigBits < val.leastSigBits ? -1 : //
+                                (this.leastSigBits > val.leastSigBits ? 1 : //
+                                        0))));
+    }
+
+    // -------------------------------------------------------------------------------------------------------------------
+    // Private method start
+    /**
+     * 返回指定数字对应的hex值
+     *
+     * @param val 值
+     * @param digits 位
+     * @return 值
+     */
+    private static String digits(long val, int digits)
+    {
+        long hi = 1L << (digits * 4);
+        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
+    }
+
+    /**
+     * 检查是否为time-based版本UUID
+     */
+    private void checkTimeBase()
+    {
+        if (version() != 1)
+        {
+            throw new UnsupportedOperationException("Not a time-based UUID");
+        }
+    }
+
+    /**
+     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
+     *
+     * @return {@link SecureRandom}
+     */
+    public static SecureRandom getSecureRandom()
+    {
+        try
+        {
+            return SecureRandom.getInstance("SHA1PRNG");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new UtilException(e);
+        }
+    }
+
+    /**
+     * 获取随机数生成器对象<br>
+     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
+     *
+     * @return {@link ThreadLocalRandom}
+     */
+    public static ThreadLocalRandom getRandom()
+    {
+        return ThreadLocalRandom.current();
+    }
+}

+ 4 - 0
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessType.java

@@ -55,4 +55,8 @@ public enum BusinessType {
      * 清空数据
      */
     CLEAN,
+    /**
+     * 认证
+     */
+    AUTHENTICATIONUSER,
 }

+ 10 - 1
ruoyi-common/ruoyi-common-oss/pom.xml

@@ -60,7 +60,16 @@
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>s3-transfer-manager</artifactId>
         </dependency>
-
+        <!--本地图片上传-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <!--本地图片上传结束-->
     </dependencies>
 
 </project>

+ 2 - 2
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/constant/OssConstant.java

@@ -25,12 +25,12 @@ public interface OssConstant {
     /**
      * 系统数据ids
      */
-    List<Long> SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L);
+    List<Long> SYSTEM_DATA_IDS = Arrays.asList(1L);
 
     /**
      * 云服务商
      */
-    String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"};
+    String[] CLOUD_SERVICE = new String[] {"aliyun"};
 
     /**
      * https 状态

+ 4 - 0
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java

@@ -15,6 +15,10 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 public class CaptchaProperties {
 
     private Boolean enable;
+    /**
+     * 短信验证码是否开启
+     */
+    private Boolean smsEnable;
 
     /**
      * 验证码类型

+ 1 - 1
ruoyi-modules/pom.xml

@@ -10,7 +10,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <modules>
-        <module>ruoyi-demo</module>
+<!--        <module>ruoyi-demo</module>-->
         <module>ruoyi-generator</module>
         <module>ruoyi-job</module>
         <module>ruoyi-system</module>

+ 1 - 1
ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml

@@ -1,7 +1,7 @@
 # 代码生成
 gen:
   # 作者
-  author: Lion Li
+  author: boman
   # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
   packageName: org.dromara.system
   # 自动去除表前缀,默认是false

+ 2 - 2
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm

@@ -95,7 +95,7 @@ public class ${ClassName}Controller extends BaseController {
     @SaCheckPermission("${permissionPrefix}:edit")
     @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
-    @PutMapping()
+    @PostMapping("/put")
     public R<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
         return toAjax(${className}Service.updateByBo(bo));
     }
@@ -107,7 +107,7 @@ public class ${ClassName}Controller extends BaseController {
      */
     @SaCheckPermission("${permissionPrefix}:remove")
     @Log(title = "${functionName}", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{${pkColumn.javaField}s}")
+    @GetMapping("/delete/{${pkColumn.javaField}s}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
         return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true));

+ 4 - 4
ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm

@@ -45,8 +45,8 @@ export const add${BusinessName} = (data: ${BusinessName}Form) => {
  */
 export const update${BusinessName} = (data: ${BusinessName}Form) => {
   return request({
-    url: '/${moduleName}/${businessName}',
-    method: 'put',
+    url: '/put/${moduleName}/${businessName}',
+    method: 'post',
     data: data
   });
 };
@@ -57,7 +57,7 @@ export const update${BusinessName} = (data: ${BusinessName}Form) => {
  */
 export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array<string | number>) => {
   return request({
-    url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
-    method: 'delete'
+    url: '/delete/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+    method: 'get'
   });
 };

+ 17 - 16
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java

@@ -1,26 +1,27 @@
 package org.dromara.system.controller.system;
 
-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 org.dromara.common.idempotent.annotation.RepeatSubmit;
-import org.dromara.common.log.annotation.Log;
-import org.dromara.common.web.core.BaseController;
-import org.dromara.common.mybatis.core.page.PageQuery;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.validate.AddGroup;
 import org.dromara.common.core.validate.EditGroup;
-import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.excel.utils.ExcelUtil;
-import org.dromara.system.domain.vo.SysClientVo;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
 import org.dromara.system.domain.bo.SysClientBo;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.service.ISysClientService;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
 
 /**
  * 客户端管理
@@ -85,7 +86,7 @@ public class SysClientController extends BaseController {
     @SaCheckPermission("system:client:edit")
     @Log(title = "客户端管理", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
-    @PutMapping()
+    @PostMapping("/put")
     public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysClientBo bo) {
         return toAjax(sysClientService.updateByBo(bo));
     }
@@ -107,7 +108,7 @@ public class SysClientController extends BaseController {
      */
     @SaCheckPermission("system:client:remove")
     @Log(title = "客户端管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{ids}")
+    @GetMapping("/delete/{ids}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] ids) {
         return toAjax(sysClientService.deleteWithValidByIds(List.of(ids), true));

+ 3 - 3
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java

@@ -91,7 +91,7 @@ public class SysConfigController extends BaseController {
      */
     @SaCheckPermission("system:config:edit")
     @Log(title = "参数管理", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysConfigBo config) {
         if (!configService.checkConfigKeyUnique(config)) {
             return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
@@ -105,7 +105,7 @@ public class SysConfigController extends BaseController {
      */
     @SaCheckPermission("system:config:edit")
     @Log(title = "参数管理", businessType = BusinessType.UPDATE)
-    @PutMapping("/updateByKey")
+    @PostMapping("/updateByKey")
     public R<Void> updateByKey(@RequestBody SysConfigBo config) {
         configService.updateConfig(config);
         return R.ok();
@@ -118,7 +118,7 @@ public class SysConfigController extends BaseController {
      */
     @SaCheckPermission("system:config:remove")
     @Log(title = "参数管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{configIds}")
+    @GetMapping("/delete/{configIds}")
     public R<Void> remove(@PathVariable Long[] configIds) {
         configService.deleteConfigByIds(configIds);
         return R.ok();

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java

@@ -86,7 +86,7 @@ public class SysDeptController extends BaseController {
      */
     @SaCheckPermission("system:dept:edit")
     @Log(title = "部门管理", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysDeptBo dept) {
         Long deptId = dept.getDeptId();
         deptService.checkDeptDataScope(deptId);
@@ -111,7 +111,7 @@ public class SysDeptController extends BaseController {
      */
     @SaCheckPermission("system:dept:remove")
     @Log(title = "部门管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{deptId}")
+    @GetMapping("/delete/{deptId}")
     public R<Void> remove(@PathVariable Long deptId) {
         if (deptService.hasChildByDeptId(deptId)) {
             return R.warn("存在下级部门,不允许删除");

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java

@@ -99,7 +99,7 @@ public class SysDictDataController extends BaseController {
      */
     @SaCheckPermission("system:dict:edit")
     @Log(title = "字典数据", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysDictDataBo dict) {
         if (!dictDataService.checkDictDataUnique(dict)) {
             return R.fail("修改字典数据'" + dict.getDictValue() + "'失败,字典键值已存在");
@@ -115,7 +115,7 @@ public class SysDictDataController extends BaseController {
      */
     @SaCheckPermission("system:dict:remove")
     @Log(title = "字典类型", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{dictCodes}")
+    @GetMapping("/delete/{dictCodes}")
     public R<Void> remove(@PathVariable Long[] dictCodes) {
         dictDataService.deleteDictDataByIds(dictCodes);
         return R.ok();

+ 3 - 3
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java

@@ -81,7 +81,7 @@ public class SysDictTypeController extends BaseController {
      */
     @SaCheckPermission("system:dict:edit")
     @Log(title = "字典类型", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysDictTypeBo dict) {
         if (!dictTypeService.checkDictTypeUnique(dict)) {
             return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
@@ -97,7 +97,7 @@ public class SysDictTypeController extends BaseController {
      */
     @SaCheckPermission("system:dict:remove")
     @Log(title = "字典类型", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{dictIds}")
+    @GetMapping("/delete/{dictIds}")
     public R<Void> remove(@PathVariable Long[] dictIds) {
         dictTypeService.deleteDictTypeByIds(dictIds);
         return R.ok();
@@ -108,7 +108,7 @@ public class SysDictTypeController extends BaseController {
      */
     @SaCheckPermission("system:dict:remove")
     @Log(title = "字典类型", businessType = BusinessType.CLEAN)
-    @DeleteMapping("/refreshCache")
+    @GetMapping("/refreshCache")
     public R<Void> refreshCache() {
         dictTypeService.resetDictCache();
         return R.ok();

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java

@@ -140,7 +140,7 @@ public class SysMenuController extends BaseController {
     @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
     @SaCheckPermission("system:menu:edit")
     @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysMenuBo menu) {
         if (!menuService.checkMenuNameUnique(menu)) {
             return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
@@ -160,7 +160,7 @@ public class SysMenuController extends BaseController {
     @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
     @SaCheckPermission("system:menu:remove")
     @Log(title = "菜单管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{menuId}")
+    @GetMapping("/delete/{menuId}")
     public R<Void> remove(@PathVariable("menuId") Long menuId) {
         if (menuService.hasChildByMenuId(menuId)) {
             return R.warn("存在子菜单,不允许删除");

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java

@@ -71,7 +71,7 @@ public class SysNoticeController extends BaseController {
      */
     @SaCheckPermission("system:notice:edit")
     @Log(title = "通知公告", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysNoticeBo notice) {
         return toAjax(noticeService.updateNotice(notice));
     }
@@ -83,7 +83,7 @@ public class SysNoticeController extends BaseController {
      */
     @SaCheckPermission("system:notice:remove")
     @Log(title = "通知公告", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{noticeIds}")
+    @GetMapping("/delete/{noticeIds}")
     public R<Void> remove(@PathVariable Long[] noticeIds) {
         return toAjax(noticeService.deleteNoticeByIds(noticeIds));
     }

+ 3 - 3
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssConfigController.java

@@ -75,7 +75,7 @@ public class SysOssConfigController extends BaseController {
     @SaCheckPermission("system:ossConfig:edit")
     @Log(title = "对象存储配置", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
-    @PutMapping()
+    @PostMapping("/put")
     public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
         return toAjax(ossConfigService.updateByBo(bo));
     }
@@ -87,7 +87,7 @@ public class SysOssConfigController extends BaseController {
      */
     @SaCheckPermission("system:ossConfig:remove")
     @Log(title = "对象存储配置", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{ossConfigIds}")
+    @GetMapping("/delete/{ossConfigIds}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] ossConfigIds) {
         return toAjax(ossConfigService.deleteWithValidByIds(List.of(ossConfigIds), true));
@@ -98,7 +98,7 @@ public class SysOssConfigController extends BaseController {
      */
     @SaCheckPermission("system:ossConfig:edit")
     @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
-    @PutMapping("/changeStatus")
+    @PostMapping("/changeStatus")
     public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {
         return toAjax(ossConfigService.updateOssConfigStatus(bo));
     }

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java

@@ -99,7 +99,7 @@ public class SysOssController extends BaseController {
      */
     @SaCheckPermission("system:oss:remove")
     @Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{ossIds}")
+    @GetMapping("/delete/{ossIds}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] ossIds) {
         return toAjax(ossService.deleteWithValidByIds(List.of(ossIds), true));

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java

@@ -85,7 +85,7 @@ public class SysPostController extends BaseController {
      */
     @SaCheckPermission("system:post:edit")
     @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysPostBo post) {
         if (!postService.checkPostNameUnique(post)) {
             return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
@@ -105,7 +105,7 @@ public class SysPostController extends BaseController {
      */
     @SaCheckPermission("system:post:remove")
     @Log(title = "岗位管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{postIds}")
+    @GetMapping("/delete/{postIds}")
     public R<Void> remove(@PathVariable Long[] postIds) {
         return toAjax(postService.deletePostByIds(postIds));
     }

+ 39 - 4
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java

@@ -1,9 +1,12 @@
 package org.dromara.system.controller.system;
 
+import cn.dev33.satoken.annotation.SaIgnore;
 import cn.dev33.satoken.secure.BCrypt;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.io.FileUtil;
 import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.ObjectUtils;
+import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.core.utils.file.MimeTypeUtils;
@@ -12,6 +15,7 @@ import org.dromara.common.idempotent.annotation.RepeatSubmit;
 import org.dromara.common.log.annotation.Log;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.mybatis.helper.DataPermissionHelper;
+import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.common.web.core.BaseController;
 import org.dromara.system.domain.bo.SysUserBo;
@@ -30,6 +34,8 @@ import org.springframework.web.multipart.MultipartFile;
 
 import java.util.Arrays;
 
+import static org.dromara.common.core.constant.Constants.THR;
+
 /**
  * 个人信息 业务处理
  *
@@ -62,7 +68,7 @@ public class SysProfileController extends BaseController {
      */
     @RepeatSubmit
     @Log(title = "个人信息", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> updateProfile(@Validated @RequestBody SysUserProfileBo profile) {
         SysUserBo user = BeanUtil.toBean(profile, SysUserBo.class);
         user.setUserId(LoginHelper.getUserId());
@@ -70,9 +76,9 @@ public class SysProfileController extends BaseController {
         if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
             return R.fail("修改用户'" + username + "'失败,手机号码已存在");
         }
-        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
+/*        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
             return R.fail("修改用户'" + username + "'失败,邮箱账号已存在");
-        }
+        }*/
         int rows = DataPermissionHelper.ignore(() -> userService.updateUserProfile(user));
         if (rows > 0) {
             return R.ok();
@@ -88,7 +94,7 @@ public class SysProfileController extends BaseController {
     @RepeatSubmit
     @ApiEncrypt
     @Log(title = "个人信息", businessType = BusinessType.UPDATE)
-    @PutMapping("/updatePwd")
+    @PostMapping("/updatePwd")
     public R<Void> updatePwd(@Validated @RequestBody SysUserPasswordBo bo) {
         SysUserVo user = userService.selectUserById(LoginHelper.getUserId());
         String password = user.getPassword();
@@ -105,6 +111,35 @@ public class SysProfileController extends BaseController {
         return R.fail("修改密码异常,请联系管理员");
     }
 
+    /**
+     * 忘记密码校验验证码,修改密码
+     */
+    @RepeatSubmit
+    @ApiEncrypt
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @SaIgnore
+    @GetMapping("/appCheck")
+    public R<Void> appCheck(@RequestParam("code") String code, @RequestParam("phone") String phone, @RequestParam("password") String password) {
+        String verifyKey = GlobalConstants.SMS_CAPTCHA_CODE_KEY + THR + ":" + StringUtils.blankToDefault(phone, "");
+        String captcha = RedisUtils.getCacheObject(verifyKey);
+        if (ObjectUtils.isNotEmpty(captcha)) {
+            if (code.equals(captcha)) {
+                RedisUtils.deleteObject(verifyKey);
+                SysUserBo sysUserBo = new SysUserBo();
+                sysUserBo.setPassword(password);
+                if (!userService.checkStrongPwd(sysUserBo)) {
+                    return R.fail("密码必须包含数字、大小写字母、特殊符号且大于8位");
+                }
+                //修改密码
+                int rows = DataPermissionHelper.ignore(() -> userService.resetUserPwd(phone, BCrypt.hashpw(password)));
+                if (rows > 0) {
+                    return R.ok();
+                }
+            }
+        }
+        return R.fail("重置密码异常,请联系管理员");
+    }
+
     /**
      * 头像上传
      *

+ 7 - 7
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java

@@ -94,7 +94,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysRoleBo role) {
         roleService.checkRoleAllowed(role);
         roleService.checkRoleDataScope(role.getRoleId());
@@ -116,7 +116,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.UPDATE)
-    @PutMapping("/dataScope")
+    @PostMapping("/dataScope")
     public R<Void> dataScope(@RequestBody SysRoleBo role) {
         roleService.checkRoleAllowed(role);
         roleService.checkRoleDataScope(role.getRoleId());
@@ -128,7 +128,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.UPDATE)
-    @PutMapping("/changeStatus")
+    @PostMapping("/changeStatus")
     public R<Void> changeStatus(@RequestBody SysRoleBo role) {
         roleService.checkRoleAllowed(role);
         roleService.checkRoleDataScope(role.getRoleId());
@@ -142,7 +142,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:remove")
     @Log(title = "角色管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{roleIds}")
+    @GetMapping("/delete/{roleIds}")
     public R<Void> remove(@PathVariable Long[] roleIds) {
         return toAjax(roleService.deleteRoleByIds(roleIds));
     }
@@ -181,7 +181,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.GRANT)
-    @PutMapping("/authUser/cancel")
+    @PostMapping("/authUser/cancel")
     public R<Void> cancelAuthUser(@RequestBody SysUserRole userRole) {
         return toAjax(roleService.deleteAuthUser(userRole));
     }
@@ -194,7 +194,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.GRANT)
-    @PutMapping("/authUser/cancelAll")
+    @PostMapping("/authUser/cancelAll")
     public R<Void> cancelAuthUserAll(Long roleId, Long[] userIds) {
         return toAjax(roleService.deleteAuthUsers(roleId, userIds));
     }
@@ -207,7 +207,7 @@ public class SysRoleController extends BaseController {
      */
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.GRANT)
-    @PutMapping("/authUser/selectAll")
+    @PostMapping("/authUser/selectAll")
     public R<Void> selectAuthUserAll(Long roleId, Long[] userIds) {
         roleService.checkRoleDataScope(roleId);
         return toAjax(roleService.insertAuthUsers(roleId, userIds));

+ 3 - 3
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java

@@ -103,7 +103,7 @@ public class SysTenantController extends BaseController {
     @SaCheckPermission("system:tenant:edit")
     @Log(title = "租户管理", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
-    @PutMapping()
+    @PostMapping("/put")
     public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTenantBo bo) {
         tenantService.checkTenantAllowed(bo.getTenantId());
         if (!tenantService.checkCompanyNameUnique(bo)) {
@@ -118,7 +118,7 @@ public class SysTenantController extends BaseController {
     @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
     @SaCheckPermission("system:tenant:edit")
     @Log(title = "租户管理", businessType = BusinessType.UPDATE)
-    @PutMapping("/changeStatus")
+    @PostMapping("/changeStatus")
     public R<Void> changeStatus(@RequestBody SysTenantBo bo) {
         tenantService.checkTenantAllowed(bo.getTenantId());
         return toAjax(tenantService.updateTenantStatus(bo));
@@ -132,7 +132,7 @@ public class SysTenantController extends BaseController {
     @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
     @SaCheckPermission("system:tenant:remove")
     @Log(title = "租户管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{ids}")
+    @GetMapping("/delete/{ids}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] ids) {
         return toAjax(tenantService.deleteWithValidByIds(List.of(ids), true));

+ 3 - 3
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java

@@ -107,7 +107,7 @@ public class SysTenantPackageController extends BaseController {
     @SaCheckPermission("system:tenantPackage:edit")
     @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
-    @PutMapping()
+    @PostMapping("/put")
     public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTenantPackageBo bo) {
         if (!tenantPackageService.checkPackageNameUnique(bo)) {
             return R.fail("修改套餐'" + bo.getPackageName() + "'失败,套餐名称已存在");
@@ -121,7 +121,7 @@ public class SysTenantPackageController extends BaseController {
     @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
     @SaCheckPermission("system:tenantPackage:edit")
     @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
-    @PutMapping("/changeStatus")
+    @PostMapping("/changeStatus")
     public R<Void> changeStatus(@RequestBody SysTenantPackageBo bo) {
         return toAjax(tenantPackageService.updatePackageStatus(bo));
     }
@@ -134,7 +134,7 @@ public class SysTenantPackageController extends BaseController {
     @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
     @SaCheckPermission("system:tenantPackage:remove")
     @Log(title = "租户套餐", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{packageIds}")
+    @GetMapping("/delete/{packageIds}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] packageIds) {
         return toAjax(tenantPackageService.deleteWithValidByIds(List.of(packageIds), true));

+ 21 - 9
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java

@@ -117,6 +117,13 @@ public class SysUserController extends BaseController {
         userInfoVo.setUser(user);
         userInfoVo.setPermissions(loginUser.getMenuPermission());
         userInfoVo.setRoles(loginUser.getRolePermission());
+        //判断是否需要进行用户认证
+        boolean enable = TenantHelper.isEnable();
+        if (enable) {
+            if (StringUtils.isBlank(user.getTenantId())) {
+                userInfoVo.setAuthenticationUser("Y");
+            }
+        }
         return R.ok(userInfoVo);
     }
 
@@ -161,8 +168,10 @@ public class SysUserController extends BaseController {
             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)) {
+        } /*else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
             return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+        }*/ else if (!userService.checkStrongPwd(user)) {
+            return R.fail("密码必须包含数字、大小写字母、特殊符号且大于8位");
         }
         if (TenantHelper.isEnable()) {
             if (!tenantService.checkAccountBalance(TenantHelper.getTenantId())) {
@@ -178,18 +187,18 @@ public class SysUserController extends BaseController {
      */
     @SaCheckPermission("system:user:edit")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public R<Void> edit(@Validated @RequestBody SysUserBo user) {
-        userService.checkUserAllowed(user.getUserId());
+        //userService.checkUserAllowed(user.getUserId());
         userService.checkUserDataScope(user.getUserId());
         deptService.checkDeptDataScope(user.getDeptId());
         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)) {
+        }/* else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
             return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
-        }
+        }*/
         return toAjax(userService.updateUser(user));
     }
 
@@ -200,7 +209,7 @@ public class SysUserController extends BaseController {
      */
     @SaCheckPermission("system:user:remove")
     @Log(title = "用户管理", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{userIds}")
+    @GetMapping("/delete/{userIds}")
     public R<Void> remove(@PathVariable Long[] userIds) {
         if (ArrayUtil.contains(userIds, LoginHelper.getUserId())) {
             return R.fail("当前用户不能删除");
@@ -227,8 +236,11 @@ public class SysUserController extends BaseController {
     @ApiEncrypt
     @SaCheckPermission("system:user:resetPwd")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
-    @PutMapping("/resetPwd")
+    @PostMapping("/resetPwd")
     public R<Void> resetPwd(@RequestBody SysUserBo user) {
+        if (!userService.checkStrongPwd(user)) {
+            return R.fail("密码必须包含数字、大小写字母、特殊符号且大于8位");
+        }
         userService.checkUserAllowed(user.getUserId());
         userService.checkUserDataScope(user.getUserId());
         user.setPassword(BCrypt.hashpw(user.getPassword()));
@@ -240,7 +252,7 @@ public class SysUserController extends BaseController {
      */
     @SaCheckPermission("system:user:edit")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
-    @PutMapping("/changeStatus")
+    @PostMapping("/changeStatus")
     public R<Void> changeStatus(@RequestBody SysUserBo user) {
         userService.checkUserAllowed(user.getUserId());
         userService.checkUserDataScope(user.getUserId());
@@ -272,7 +284,7 @@ public class SysUserController extends BaseController {
      */
     @SaCheckPermission("system:user:edit")
     @Log(title = "用户管理", businessType = BusinessType.GRANT)
-    @PutMapping("/authRole")
+    @PostMapping("/authRole")
     public R<Void> insertAuthRole(Long userId, Long[] roleIds) {
         userService.checkUserDataScope(userId);
         userService.insertUserAuth(userId, roleIds);

+ 31 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserTenant.java

@@ -0,0 +1,31 @@
+package org.dromara.system.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 用户账号-租户对象 sys_user_tenant
+ *
+ * @author boman
+ * @date 2025-04-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_user_tenant")
+public class SysUserTenant extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户id
+     */
+    @TableId(value = "user_id")
+    private Long userId;
+
+
+}

+ 4 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java

@@ -97,6 +97,10 @@ public class SysUserBo extends BaseEntity {
      * 岗位组
      */
     private Long[] postIds;
+    /**
+     * 租户组
+     */
+    private String[] tenantIds;
 
     /**
      * 数据权限 当前角色ID

+ 29 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserTenantBo.java

@@ -0,0 +1,29 @@
+package org.dromara.system.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.system.domain.SysUserTenant;
+
+/**
+ * 用户账号-租户业务对象 sys_user_tenant
+ *
+ * @author boman
+ * @date 2025-04-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysUserTenant.class, reverseConvertGenerate = false)
+public class SysUserTenantBo extends BaseEntity {
+
+    /**
+     * 用户id
+     */
+    @NotNull(message = "用户id不能为空", groups = { EditGroup.class })
+    private Long userId;
+
+
+}

+ 35 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserTenantVo.java

@@ -0,0 +1,35 @@
+package org.dromara.system.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.system.domain.SysUserTenant;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+
+/**
+ * 用户账号-租户视图对象 sys_user_tenant
+ *
+ * @author boman
+ * @date 2025-04-09
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysUserTenant.class)
+public class SysUserTenantVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户id
+     */
+    @ExcelProperty(value = "用户id")
+    private Long userId;
+
+
+}

+ 4 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/UserInfoVo.java

@@ -26,5 +26,9 @@ public class UserInfoVo {
      * 角色权限
      */
     private Set<String> roles;
+    /**
+     * 用户是否需要认证(已经注册账号但是没有选择租户)
+     */
+    private String authenticationUser;
 
 }

+ 15 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserTenantMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.system.mapper;
+
+import org.dromara.system.domain.SysUserTenant;
+import org.dromara.system.domain.vo.SysUserTenantVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 用户账号-租户Mapper接口
+ *
+ * @author boman
+ * @date 2025-04-09
+ */
+public interface SysUserTenantMapper extends BaseMapperPlus<SysUserTenant, SysUserTenantVo> {
+
+}

+ 23 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java

@@ -108,6 +108,13 @@ public interface ISysUserService {
      */
     boolean checkUserNameUnique(SysUserBo user);
 
+    /**
+     * 校验强密码
+     * @param user
+     * @return
+     */
+    public boolean checkStrongPwd(SysUserBo user);
+
     /**
      * 校验手机号码是否唯一
      *
@@ -179,6 +186,14 @@ public interface ISysUserService {
      */
     int updateUserStatus(Long userId, String status);
 
+    /**
+     * 修改用户租户
+     * @param userId 用户id
+     * @param tenantId 租户id
+     * @return
+     */
+    int authenticationUser(Long userId, String tenantId);
+
     /**
      * 修改用户基本信息
      *
@@ -204,6 +219,14 @@ public interface ISysUserService {
      * @return 结果
      */
     int resetUserPwd(Long userId, String password);
+    /**
+     * 重置用户密码
+     *
+     * @param phonenumber   手机号
+     * @param password 密码
+     * @return 结果
+     */
+    int resetUserPwd(String phonenumber, String password);
 
     /**
      * 通过用户ID删除用户

+ 69 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserTenantService.java

@@ -0,0 +1,69 @@
+package org.dromara.system.service;
+
+import org.dromara.system.domain.SysUserTenant;
+import org.dromara.system.domain.vo.SysUserTenantVo;
+import org.dromara.system.domain.bo.SysUserTenantBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 用户账号-租户Service接口
+ *
+ * @author boman
+ * @date 2025-04-09
+ */
+public interface ISysUserTenantService {
+
+    /**
+     * 查询用户账号-租户
+     *
+     * @param userId 主键
+     * @return 用户账号-租户
+     */
+    SysUserTenantVo queryById(Long userId);
+
+    /**
+     * 分页查询用户账号-租户列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 用户账号-租户分页列表
+     */
+    TableDataInfo<SysUserTenantVo> queryPageList(SysUserTenantBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的用户账号-租户列表
+     *
+     * @param bo 查询条件
+     * @return 用户账号-租户列表
+     */
+    List<SysUserTenantVo> queryList(SysUserTenantBo bo);
+
+    /**
+     * 新增用户账号-租户
+     *
+     * @param bo 用户账号-租户
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(SysUserTenant bo);
+
+    /**
+     * 修改用户账号-租户
+     *
+     * @param bo 用户账号-租户
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(SysUserTenantBo bo);
+
+    /**
+     * 校验并批量删除用户账号-租户信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java

@@ -216,9 +216,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
      */
     @Override
     public void checkRoleAllowed(SysRoleBo role) {
-        if (ObjectUtil.isNotNull(role.getRoleId()) && LoginHelper.isSuperAdmin(role.getRoleId())) {
+/*        if (ObjectUtil.isNotNull(role.getRoleId()) && LoginHelper.isSuperAdmin(role.getRoleId())) {
             throw new ServiceException("不允许操作超级管理员角色");
-        }
+        }*/
         String[] keys = new String[]{TenantConstants.SUPER_ADMIN_ROLE_KEY, TenantConstants.TENANT_ADMIN_ROLE_KEY};
         // 新增不允许使用 管理员标识符
         if (ObjectUtil.isNull(role.getRoleId())

+ 65 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

@@ -22,6 +22,7 @@ import org.dromara.common.core.utils.*;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.*;
 import org.dromara.system.domain.bo.SysUserBo;
 import org.dromara.system.domain.vo.SysPostVo;
@@ -56,6 +57,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
     private final SysPostMapper postMapper;
     private final SysUserRoleMapper userRoleMapper;
     private final SysUserPostMapper userPostMapper;
+    private final SysUserTenantMapper sysUserTenantMapper;
 
     @Override
     public TableDataInfo<SysUserVo> selectPageUserList(SysUserBo user, PageQuery pageQuery) {
@@ -234,6 +236,31 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
         return !exist;
     }
 
+    /**
+     * 校验强密码
+     *
+     * @param user
+     * @return
+     */
+    @Override
+    public boolean checkStrongPwd(SysUserBo user) {
+        String pwd = user.getPassword();
+        boolean result = true;
+        try {
+            if (!PwdCheckUtil.checkPasswordLength(pwd, "8", "16")
+                || !PwdCheckUtil.checkContainLowerCase(pwd)
+                || !PwdCheckUtil.checkContainUpperCase(pwd)
+                || !PwdCheckUtil.checkContainDigit(pwd)
+                || !PwdCheckUtil.checkContainSpecialChar(pwd)
+            ) {
+                result = false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
     /**
      * 校验手机号码是否唯一
      *
@@ -307,6 +334,13 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
         insertUserPost(user, false);
         // 新增用户与角色管理
         insertUserRole(user, false);
+        // 新增用户账号与租户
+        SysUserVo sysUserVo = baseMapper.selectVoById(sysUser.getUserId());
+
+        boolean enable = TenantHelper.isEnable();
+        if (enable) {
+            sysUserTenantMapper.insert(MapstructUtils.convert(sysUserVo, SysUserTenant.class));
+        }
         return rows;
     }
 
@@ -375,6 +409,22 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
                 .eq(SysUser::getUserId, userId));
     }
 
+    /**
+     * 修改用户租户
+     *
+     * @param userId   用户id
+     * @param tenantId 租户id
+     * @return
+     */
+    @Override
+    public int authenticationUser(Long userId, String tenantId) {
+
+        return baseMapper.update(null,
+            new LambdaUpdateWrapper<SysUser>()
+                .set(SysUser::getTenantId, tenantId)
+                .eq(SysUser::getUserId, userId));
+    }
+
     /**
      * 修改用户基本信息
      *
@@ -423,6 +473,21 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
                 .eq(SysUser::getUserId, userId));
     }
 
+    /**
+     * 重置用户密码
+     *
+     * @param phonenumber 手机号
+     * @param password    密码
+     * @return 结果
+     */
+    @Override
+    public int resetUserPwd(String phonenumber, String password) {
+        return baseMapper.update(null,
+            new LambdaUpdateWrapper<SysUser>()
+                .set(SysUser::getPassword, password)
+                .eq(SysUser::getPhonenumber, phonenumber));
+    }
+
     /**
      * 新增用户角色信息
      *

+ 124 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserTenantServiceImpl.java

@@ -0,0 +1,124 @@
+package org.dromara.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 lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.system.domain.SysUserTenant;
+import org.dromara.system.domain.bo.SysUserTenantBo;
+import org.dromara.system.domain.vo.SysUserTenantVo;
+import org.dromara.system.mapper.SysUserTenantMapper;
+import org.dromara.system.service.ISysUserTenantService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 用户账号-租户Service业务层处理
+ *
+ * @author boman
+ * @date 2025-04-09
+ */
+@RequiredArgsConstructor
+@Service
+public class SysUserTenantServiceImpl implements ISysUserTenantService {
+
+    private final SysUserTenantMapper baseMapper;
+
+    /**
+     * 查询用户账号-租户
+     *
+     * @param userId 主键
+     * @return 用户账号-租户
+     */
+    @Override
+    public SysUserTenantVo queryById(Long userId){
+        return baseMapper.selectVoById(userId);
+    }
+
+    /**
+     * 分页查询用户账号-租户列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 用户账号-租户分页列表
+     */
+    @Override
+    public TableDataInfo<SysUserTenantVo> queryPageList(SysUserTenantBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SysUserTenant> lqw = buildQueryWrapper(bo);
+        Page<SysUserTenantVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的用户账号-租户列表
+     *
+     * @param bo 查询条件
+     * @return 用户账号-租户列表
+     */
+    @Override
+    public List<SysUserTenantVo> queryList(SysUserTenantBo bo) {
+        LambdaQueryWrapper<SysUserTenant> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<SysUserTenant> buildQueryWrapper(SysUserTenantBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<SysUserTenant> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(SysUserTenant::getUserId);
+        lqw.orderByAsc(SysUserTenant::getTenantId);
+        return lqw;
+    }
+
+    /**
+     * 新增用户账号-租户
+     *
+     * @param add 用户账号-租户
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(SysUserTenant add) {
+        boolean flag = baseMapper.insert(add) > 0;
+        return flag;
+    }
+
+    /**
+     * 修改用户账号-租户
+     *
+     * @param bo 用户账号-租户
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(SysUserTenantBo bo) {
+        SysUserTenant update = MapstructUtils.convert(bo, SysUserTenant.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(SysUserTenant entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除用户账号-租户信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 7 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserTenantMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.system.mapper.SysUserTenantMapper">
+
+</mapper>

文件差异内容过多而无法显示
+ 803 - 404
script/sql/ry_vue_5.X.sql


部分文件因为文件数量过多而无法显示