8 Commits

Author SHA1 Message Date
luoqiz
2bb2f96857 refactor: 增加租户查询条件 (#181) 2025-08-06 09:49:57 +08:00
df6e294cbd fix: 修复缓存 CRUD API 权限前缀偶发性缺失元素的问题 2025-08-04 20:56:44 +08:00
3551d16f24 docs: 更新 README 参与贡献部分内容 2025-08-03 18:31:05 +08:00
7ad12effae fix(system/file): 修复上传文件不指定 parentPath 默认上级目录不自动创建的问题 2025-07-30 20:30:51 +08:00
e4f4554eef docs: 更新 README 介绍部分内容 2025-07-29 22:59:55 +08:00
6129b9fd1f docs: 更新 README 项目结构部分内容 2025-07-28 21:16:13 +08:00
7f05453d8c fix(generator): 修复代码生成前端 API 模板错误 2025-07-28 20:16:34 +08:00
lishuyanla
93d8168e9f fix(system/user): 系统内置用户禁止修改角色
Co-authored-by: lishuyan<1206770390@qq.com>



# message auto-generated for no-merge-commit merge:
merge feat/dev into dev

fix(user-role): 🐛 禁止修改系统内置用户的角色分配

Created-by: lishuyanla
Commit-by: lishuyan
Merged-by: Charles_7c
Description: <!--
  非常感谢您的 PR!在提交之前,请务必确保您 PR 的代码经过了完整测试,并且通过了代码规范检查。
-->

<!-- 在 [] 中输入 x 来勾选) -->

## PR 类型

<!-- 您的 PR 引入了哪种类型的变更? -->
<!-- 只支持选择一种类型,如果有多种类型,可以在更新日志中增加 “类型” 列。 -->

- [ ] 新 feature
- [x] Bug 修复
- [ ] 功能增强
- [ ] 文档变更
- [ ] 代码样式变更
- [ ] 重构
- [ ] 性能改进
- [ ] 单元测试
- [ ] CI/CD
- [ ] 其他

## PR 目的

<!-- 描述一下您的 PR 解决了什么问题。如果可以,请链接到相关 issues。 -->

修复 可以修改系统内置用户的角色分配 的BUG

## 解决方案

<!-- 详细描述您是如何解决的问题 -->

## PR 测试

<!-- 如果可以,请为您的 PR 添加或更新单元测试。 -->
<!-- 请描述一下您是如何测试 PR 的。例如:创建/更新单元测试或添加相关的截图。 -->

## Changelog

| 模块         | Changelog                                                    | Related issues |
| ------------ | ------------------------------------------------------------ | -------------- |
| 系统管理模块 | fix(user-role): 🐛 禁止修改系统内置用户的角色分配<br/><br/>- 在用户角色分配功能中增加了对系统内置用户的检查<br/>- 如果尝试修改系统内置用户的角色,会抛出异常并提示错误信息<br/>- 这个修改确保了系统内置用户的权限不会被意外更改,提高了系统安全性 |                |

<!-- 如果有多种类型的变更,可以在变更日志表中增加 “类型” 列,该列的值与上方 “PR 类型” 相同。 -->
<!-- Related issues 格式为 Closes #<issue号>,或者 Fixes #<issue号>,或者 Resolves #<issue号>。 -->

## 其他信息

<!-- 请描述一下还有哪些注意事项。例如:如果引入了一个不向下兼容的变更,请描述其影响。 -->

## 提交前确认

- [x] PR 代码经过了完整测试,并且通过了代码规范检查
- [x] 已经完整填写 Changelog,并链接到了相关 issues
- [x] PR 代码将要提交到 dev 分支

See merge request: continew/continew-admin!8
2025-07-28 17:31:22 +08:00
12 changed files with 115 additions and 84 deletions

View File

@@ -17,7 +17,7 @@ body:
required: true
- label: 查阅过 [使用指南](https://continew.top/admin/backend/structure.html) 和 [常见问题](https://continew.top/admin/faq.html) ,仍然认为很有必要
required: true
- label: 查阅过 [需求墙](https://continew.top/admin/other/feature.html),仍没有该功能计划
- label: 查阅过 [需求墙](https://continew.top/admin/feature.html),仍没有该功能计划
required: true
- label: 搜索了项目 Issues没有其他人提交过类似的 Feature如果对应 Feature 尚未实现,您可以先订阅关注该 Issue为了方便后来者查找问题解决方法请避免创建重复的 Issue
required: true

128
README.md
View File

@@ -84,11 +84,13 @@ ContiNew AdminContinue New Admin页面现代美观且专注设计与
**AI 编程纪元已经开启,基于 ContiNew 项目开发,让 AI 助手“学习”更优雅的代码规范,“写出”更优质的代码。**
1.**甄选技术栈:** ContiNewContinue New 项目致力于持续迭代优化,让技术不掉队。在技术选型时,进行深度广泛地调研,从流行度、成熟度和发展潜力等多方面甄选技术栈
**1.长期稳定:** 自 2022 年 12 月 8 日创建2023 年 3 月 26 日发布 v1.0.0截至今日ContiNew Admin 已累计发布 25 个版本ContiNew Starter 已累计发布 43 个版本
2.**Starter 组件:** 从 v2.1.0 版本开始,抽取并封装后端基础组件及各框架集成配置到 ContiNew Starter 项目,且 **[已发布至 Maven 中央仓库](https://central.sonatype.com/search?q=continew-starter&namespace=top.continew)**,可在你的任意项目中直接引入所需依赖使用。即使你不用脚手架项目,难道能让你搭项目框架更快、更爽、更省力的 Starter 也要 Say No 吗?
**2.甄选技术栈:** ContiNewContinue New 项目致力于持续迭代优化,确保技术栈紧跟时代。在技术选型时,我们进行了深度广泛的调研,从流行度、成熟度和发展潜力等多维度精心挑选技术栈。
3.**CRUD 套件:** 封装通用增删改查套件,适配后端各分层,几分钟即可提供一套 CRUD API包括新增、修改、批量删除、查询详情、分页列表查询、全部列表查询、树型列表查询、导出到 Excel甚至是字典列表用于下拉选项场景且 API 支持按实际所需开放或扩展。
**3.Starter 组件:** 从 v2.1.0 版本开始,我们将后端基础组件及各框架集成配置抽取并封装到 ContiNew Starter 项目中,极大降低上手和升级难度。且 **[已发布至 Maven 中央仓库](https://central.sonatype.com/search?q=continew-starter&namespace=top.continew)**,你可以在任意项目中直接引入所需依赖使用。即使你不使用完整的中后台框架,这些能让你搭项目框架更快、更爽、更省力的 Starter 组件,难道不香吗?
**4.CRUD 套件:** 封装通用增删改查套件,适配后端各分层架构,几分钟即可提供一套完整的 CRUD API包括新增、修改、批量删除、查询详情、分页列表、全部列表、树型列表、Excel 导出,甚至是字典列表(用于下拉选项场景)。所有 API 均可根据实际需求灵活开放或扩展。
```java
@Tag(name = "部门管理 API")
@@ -97,35 +99,42 @@ ContiNew AdminContinue New Admin页面现代美观且专注设计与
public class DeptController extends BaseController<DeptService, DeptResp, DeptDetailResp, DeptQuery, DeptReq> {}
```
4.**代码生成器:** 提供代码生成器,配套前后端代码生成模板数据表设计完后,简单配置一下即可生成前后端 80% 的代码,包 CRUD API、权限控制、参数校验、接口文档等内容。如果业务不复杂,也可能就是 95% 的代码。
**5.代码生成器:** 同步提供代码生成器,配套前后端代码生成模板数据表设计完后,简单配置即可生成前后端 80% 的代码,包 CRUD API、权限控制、参数校验、接口文档等内容。业务不复杂,甚至能覆盖 95% 的代码
5.**改善开发体验:** 持续优化适配能改善开发体验的组件。
**6.提升开发体验:** 持续优化适配各类能提升开发体验的组件。
- 适配 ContiNew Starter 组件针对 Spring 基础配置、通用解决方案及流行框架进行深度封装的 starter 集合,改善你开发每个 Spring Boot Web 项目的体验时间日期及枚举参数自动转换、默认线程池、跨域、加密、脱敏、限流、幂等、License、日志、异常及响应通用解决方案等等,更多细节可查看 Starter 源码
- 适配 Crane4j 数据填充组件减少因为一个用户名而产生的联表回填
- 适配 SpEL Validator 基于 SpEL 的 Java 参数校验,使用 SpEL 表达式,强化基础参数校验。例如:当其中一个字段为 xxx 时,另一字段不能为空等等
- 适配 P6Spy SQL 性能分析组件,开发期间方便监控 SQL 执行;
- 适配 TLog 链路追踪组件,方便在杂乱的日志文件中追踪你某次请求的日志记录
- 适配 JetCache 缓存框架(比 Spring Cache 更强大易用),通过注解声明即可快速实现方法级缓存,极大改善编码式缓存体验,且支持灵活的二级缓存配置分布式自动刷新等能力
- 前端适配 Vue DevtoolsVue 官方提供的调试浏览器插件),极大提高 Vue 开发及调试效率
- ContiNew Starter 组件集合:针对 Spring 基础配置、通用解决方案及流行框架进行深度封装,改善你开发每个 Spring Boot Web 项目的体验(包含时间日期及枚举参数自动转换、默认线程池、跨域、加密、脱敏、限流、幂等、License、日志、异常及响应通用解决方案等
- Crane4j 数据填充组件减少因单个字段(如用户名而产生的联表查询
- SpEL Validator基于 SpEL 表达式的参数校验,强化复杂场景下的参数验证(如:当某字段为特定值时,另一字段不能为空
- P6Spy SQL 性能分析开发期间方便监控 SQL 执行性能
- TLog 链路追踪:在繁杂的日志中快速定位某次请求的完整日志;
- JetCache 缓存框架:通过注解即可实现方法级缓存,支持灵活的二级缓存配置分布式自动刷新;
6.**Almost最佳后端规范** 后端严格遵循阿里巴巴 Java 编码规范,注释覆盖率 > 45%,接口参数示例 100%代码分层使用体验佳,变量方法命名清晰统一,前端代码也使用严格的 ESLint、StyleLint 等检查。良好的设计,代码复用率极高!写代码时,让你有一种无需多写,理应如此的感觉。我是代码洁癖,我实际写的时候很清楚这到底是不是乱吹
**7.Almost 最佳后端规范:** 后端严格遵循阿里巴巴 Java 编码规范,注释覆盖率 > 45%,接口参数示例 100%代码分层清晰,变量方法命名统一规范,前端代码同样采用严格的 ESLint、StyleLint 等检查。优秀的设计带来极高的代码复用率!开发时,你会有一种无需多写,理应如此”的流畅感
7.**卓越工程:** 后端采用模块化工程结构,并适配了统一项目版本号、编译项目自动代码格式化等插件提供自定义打包部署结构配置(配置文件、三方依赖主程序分离),提供全套环境应用的 Docker Compose 部署脚本。为了减少您开发新项目时的改造耗时,项目品牌配置持续进行深度聚合,简单的配置和结构修改即可快速开始独属于你的新项目。我们还进行了全局 Lombok 配置,继承场景默认自动生效 @EqualsAndHashCode(callSuper = true)、@ToString(callSuper = true),不需要你手动添加了,并且主动禁用了部分 Lombok 注解,例如:@Val@Log4j...,杜绝“又菜又爱玩”的 partner 滥用。
**8.卓越工程化实践** 后端采用模块化工程结构,集成了统一版本管理、编译自动代码格式化等插件提供自定义打包部署配置(配置文件、三方依赖主程序分离),以及全套环境应用的 Docker Compose 部署脚本。
8.**业务脚手架:** 有颜有料,不止是说说而已,持续打磨 UI 设计与色彩主题。提供基于 RBAC 的权限控制、通用数据权限,包含丰富的通用业务功能:第三方登录,邮箱、短信(生产级炸弹漏洞处理方案),个人中心、用户管理、角色管理、部门管理、系统配置(基础站点配置、邮件配置、安全配置)、系统日志、消息中心、通知公告等,设计用心,逻辑合理闭环
为减少新项目开发的改造成本,我们持续深度聚合项目品牌配置,通过简单的配置和结构修改,即可快速启动你的专属项目
> 一个好的脚手架项目,不仅仅是提供一系列组件集成与配置,也不仅仅是封装一堆好用的工具,还更应该提供一系列通用基础业务解决方案及设计,为初创团队项目减负
我们还进行了全局 Lombok 配置,继承场景默认自动应用 `@EqualsAndHashCode(callSuper = true)``@ToString(callSuper = true)`,无需手动添加。同时主动禁用了部分 Lombok 注解(如 `@Val``@Log4j` 等),避免“又菜又爱玩”的 partner 滥用
9.**质量与安全:** CI 已集成 Sonar、CodacyPush 即扫描代码质量,定期扫描 CVE 漏洞及时解决潜在问题。封装数据库字段加密、JSON 脱敏、XSS 过滤等工具,提供诸多安全解决方案
**9.全能业务脚手架:** 支持 **SaaS 租户架构**,基于 RBAC 的权限控制与通用数据权限管理。精心设计的 UI 界面与色彩主题,兼具美观与实用性。内置丰富的通用业务解决方案:第三方登录、邮箱/短信服务(含生产级漏洞处理方案)、个人中心、用户管理、角色管理、组织管理、系统配置、系统日志、消息中心、通知公告等,逻辑闭环,开箱即用
由于篇幅有限,且项目正处于高速发展期,更多功能正在陆续上线(敬请关注仓库或群内动态)。另外像最基本的统一异常、错误处理,基础线程池等配置就不在此赘述,细节优化详情请 clone 代码查看
> 优秀的中后台框架不仅提供组件集成与配置,封装好用的工具,更应提供通用基础业务设计及解决方案,为初创团队减负
**10.质量与安全并重:** 我们高度重视项目质量与安全CI 已集成 Sonar、Codacy代码提交即自动扫描质量问题。定期扫描 CVE 漏洞及时解决潜在风险。封装了数据库字段加密、JSON 脱敏、XSS 过滤等工具,提供全方位的安全解决方案。
许多项目在开发或交付过程中需满足 Sonarqube 等质量指标,使用 ContiNew Admin 框架,让你从一开始就站在高质量的起点。
---
由于篇幅有限,且项目正处于高速发展期,更多功能正在持续开发中,敬请关注仓库或加入交流群了解最新动态。至于统一异常处理、错误处理、基础线程池配置(默认线程参数、线程上下文支持异步传递)等基础特性,这里不再赘述,更多细节优化欢迎克隆代码体验。
> Talk is cheap, show the code.
## 系统功能
> [!TIP]
> 更多功能和优化正在赶来💦,最新项目计划、进展请进群或关注 [需求墙](https://continew.top/admin/other/feature.html) 和 [更新日志](https://continew.top/admin/changelog/)。
> 更多功能和优化正在赶来💦,最新项目计划、进展请进群或关注 [需求墙](https://continew.top/admin/feature.html) 和 [更新日志](https://continew.top/admin/changelog/)。
> 功能不会用?请查看 [功能手册](https://continew.top/admin/function/tenant/management.html)。
- 仪表盘:提供工作台、分析页,工作台提供功能快捷导航入口、最新公告、动态;分析页提供全面数据可视化能力
@@ -292,9 +301,9 @@ continew-admin
│ │ ├─ main
│ │ │ ├─ java/top/continew/admin
│ │ │ │ ├─ config (配置)
│ │ │ │ ├─ controller
│ │ │ │ │ common通用相关 API
│ │ │ │ │ └monitor系统监控相关 API
│ │ │ │ │ ├─ log操作日志配置
│ │ │ │ │ satokenSaToken 认证配置
│ │ │ │ controller通用 API
│ │ │ │ ├─ job (定时任务)
│ │ │ │ └─ ContiNewAdminApplication.javaContiNew Admin 启动程序)
│ │ │ └─ resources
@@ -323,9 +332,11 @@ continew-admin
│ │ │ │ │ │ ├─ req系统认证相关请求参数Request
│ │ │ │ │ │ └─ resp系统认证相关响应参数Response
│ │ │ │ │ ├─ enums系统认证相关枚举
│ │ │ │ │ ├─ constant系统认证相关常量
│ │ │ │ │ ├─ handler系统认证相关处理器
│ │ │ │ │ └─ config系统认证相关配置
│ │ │ │ └─ system系统管理相关业务
│ │ │ │ ├─ api系统管理相关公共业务 API 实现)
│ │ │ │ ├─ controller系统管理相关 API
│ │ │ │ ├─ service系统管理相关业务接口及实现类
│ │ │ │ ├─ mapper系统管理相关 Mapper
@@ -335,8 +346,10 @@ continew-admin
│ │ │ │ │ ├─ req系统管理相关请求参数Request
│ │ │ │ │ └─ resp系统管理相关响应参数Response
│ │ │ │ ├─ enums系统管理相关枚举
│ │ │ │ ├─ constant系统管理相关常量
│ │ │ │ ├─ util系统管理相关工具类
│ │ │ │ ├─ validation系统管理相关参数校验工具类
│ │ │ │ ├─ container系统管理相关 Crane4j 数据填充容器配置)
│ │ │ │ └─ config系统管理相关配置
│ │ │ └─ resources
│ │ │ └─ mapper系统管理相关 Mapper XML 文件目录)
@@ -355,23 +368,26 @@ continew-admin
│ │ │ │ │ ├─ req能力开放相关请求参数Request
│ │ │ │ │ └─ resp能力开放相关响应参数Response
│ │ │ │ ├─ util能力开放相关工具类
│ │ │ │ ├─ handler能力开放相关处理器
│ │ │ │ ├─ sign能力开放相关 API 参数签名算法)
│ │ │ │ └─ config能力开放相关配置
│ │ │ └─ test测试相关代码目录
│ │ └─ pom.xml
│ ├─ continew-plugin-tenant租户插件模块
│ │ ├─ src
│ │ │ ├─ main/java/top/continew/admin/tenant
│ │ │ │ ├─ api租户相关公共业务 API 实现)
│ │ │ │ ├─ controller租户相关 API
│ │ │ │ ├─ service租户相关业务接口及实现类
│ │ │ │ ├─ mapper租户相关 Mapper
│ │ │ │ ├─ model租户相关模型
│ │ │ │ │ ├─ enums租户相关枚举
│ │ │ │ │ ├─ entity租户相关实体
│ │ │ │ │ ├─ query租户相关查询条件
│ │ │ │ │ ├─ req租户相关请求参数Request
│ │ │ │ │ └─ resp租户相关响应参数Response
│ │ │ │ ├─ util(租户相关工具类
│ │ │ │ ├─ enums(租户相关枚举
│ │ │ │ ├─ constant租户相关常量类
│ │ │ │ ├─ util租户相关工具类
│ │ │ │ └─ config租户相关配置
│ │ │ └─ test测试相关代码目录
│ │ └─ pom.xml
@@ -380,14 +396,15 @@ continew-admin
│ │ │ ├─ main/java/top/continew/admin/schedule
│ │ │ │ ├─ controller任务调度相关 API
│ │ │ │ ├─ service代码生成器相关业务接口及实现类
│ │ │ │ ├─ api任务调度中心相关 API
│ │ │ │ ├─ api任务调度中心相关 Feign API
│ │ │ │ ├─ model任务调度相关模型
│ │ │ │ │ ├─ query任务调度相关查询条件
│ │ │ │ │ ├─ req任务调度相关请求参数Request
│ │ │ │ │ └─ resp任务调度相关响应参数Response
│ │ │ │ ├─ constant任务调度相关常量类
│ │ │ │ ├─ enums任务调度相关枚举
│ │ │ │ ├─ constant任务调度相关常量类
│ │ │ │ ├─ exception任务调度相关异常
│ │ │ │ ├─ annotation任务调度相关注解
│ │ │ │ └─ config任务调度相关配置
│ │ │ └─ test测试相关代码目录
│ │ └─ pom.xml
@@ -415,6 +432,7 @@ continew-admin
├─ continew-common公共模块存放公共工具类公共配置等
│ ├─ src
│ │ ├─ main/java/top/continew/admin/common
│ │ │ ├─ api公共业务 API
│ │ │ ├─ base公共基类
│ │ │ │ ├─ controller控制器基类
│ │ │ │ ├─ mapperMapper 接口基类)
@@ -422,14 +440,15 @@ continew-admin
│ │ │ │ │ ├─ entity实体基类
│ │ │ │ │ └─ resp列表、详情响应基类
│ │ │ │ └─ service业务接口及实现基类
│ │ │ ├─ service公共服务接口
│ │ │ ├─ model公共模型
│ │ │ │ ├─ dto公共数据传输对象DTO
│ │ │ │ └─ req公共请求参数Request
│ │ │ ├─ context公共上下文
│ │ │ ├─ constant公共常量类
│ │ │ ├─ enums公共枚举
│ │ │ ├─ constant公共常量类
│ │ │ ├─ util公共工具类
│ │ │ └─ config公共配置
│ │ │ ├─ crudCRUD 配置)
│ │ │ ├─ mybatisMyBatis Plus 配置)
│ │ │ ├─ websocketWebsocket 配置)
│ │ │ ├─ doc接口文档配置
@@ -470,53 +489,40 @@ continew-admin
└─ pom.xml包含版本锁定及全局插件相关配置
```
## 贡献指南
## 参与贡献
ContiNew Admin 致力于提供开箱即用持续舒适的开发体验。作为一个开源项目Creator 的初是希望 ContiNew Admin 依托开源协作模式,提升技术透明度、放大集体智慧、共创优秀实践,源源不断地为企业级项目开发提供助力。
ContiNewContinue New系列项目致力于通过持续迭代为开发者提供舒适的开发体验。作为开源社区我们的初是希望通过开源协作模式,提升技术透明度、放大集体智慧、共创优秀实践,源源不断地为企业级项目开发提供助力。
我们非常欢迎广大社区用户为 ContiNew Admin **贡献(开发,测试、文档、答疑等)** 或优化代码,欢迎各位感兴趣的小伙伴儿,[添加微信](https://continew.top/discussion.html) 讨论或认领任务。
我们诚挚邀请广大社区用户为 ContiNew 项目贡献力量,包括但不限于 Issue 排查、测试验证、代码开发与重构等。每一份贡献,都是推动项目进步的重要力量(请查阅 [贡献指南](https://continew.top/about/contributing.html))。欢迎各位感兴趣的小伙伴儿,[添加微信](https://continew.top/discussion.html) 讨论或认领任务。
### 分支说明
ContiNew Admin 的分支目前分为下个大版本的开发分支和上个大版本的维护分支PR 前请注意对应分支是否处于维护状态,版本支持情况请查看 [更新日志/版本支持](https://continew.top/admin/changelog/)。
ContiNew 系列项目采用清晰的分支策略,确保开发与维护有序进行。提交 PR 前,请确认目标分支是否处于活跃维护状态,版本支持情况请查看 [更新日志#版本支持](https://continew.top/admin/changelog/)。
| 分支 | 说明 |
| ----- | ------------------------------------------------------------ |
| dev | 开发分支,默认为下个大版本的 SNAPSHOT 版本,接受新功能或功能优化 PR |
| x.x.x | 维护分支,在 vx.x.x 版本维护期终止前(一般为下个大版本发布前),用于修复上个版本Bug,只接受已有功能修复,不接受新功能 PR |
| dev | 开发分支,用于下个大版本的 SNAPSHOT 开发,接受新功能或功能优化 PR |
| x.x.x | 维护分支,用于特定版本(如 vx.x.xbug 修复,仅接受已有功能修复 PR,不接受新功能 |
### 贡献代码
### 流程步骤
如果您想提交新功能或优化现有代码,可以按照以下步骤操作
若您希望提交新功能或优化现有代码,请遵循以下步骤:
1. 首先,在 Gitee 或 Github 上将项目 fork 到您自己的仓库
2. 然后,将 fork 过来的项目(即您的项目)克隆到本地
3. 基于当前仍在维护的分支(例如:dev切出来一个新的分支例如feat/tenant不要修改源分支源分支仅做同步 continew 最新代码用)
4. 在新分支开始修改代码修改完成后,将代码 commit 并 push 到您的远程仓库
5. Gitee 或 Github 上新建 pull requestpr选择好源和目标,按模板要求填写说明信息后提交即可(多多参考 [批准合并的 pr 记录](https://github.com/continew-org/continew-admin/pulls?q=is%3Apr+is%3Amerged),会大大增加批准合并率)
6. 提交后,会提示你需要签署 CLAContributor License Agreement贡献者协议)请确保你的 commit 所用邮箱和对应平台绑定邮箱一致(如果不一致,可以在本地通过 `git reset --soft HEAD~1` 回退,然后使用正确邮箱重新提交,最后 `git push -f` 即可,不需要重新创建 PR然后使用该邮箱签署即可
7. 最后,耐心等待维护者合并您的请求即可
8. PR 合并后,下次 PR 时请先同步最新代码,然后再次从步骤 3 开始
以下是向 `continew-admin` 项目提交 pull request 为例的简单步骤:
```
1.continew/continew-admin -> fork -> your/continew-admin
2.git clone your/continew-admin
3.dev -> feat/tenant
4.feat/tenant 本地开发,开发完 push 到 your/continew-admin
5.your/continew-admin:feat/tenant -> continew/continew-admin:dev
6.阅读并签署 CLA
7.PR 合并完成,删除 feat/tenant 分支
8.强制从 continew/continew-admin 覆盖更新 your/continew-admin然后重复步骤 3...
```
1. 在开源平台上将项目 fork 到您的个人仓库
2. 将 fork 的项目克隆到本地开发环境
3. 基于当前维护的分支(dev创建新分支(如 feat/newFeature请勿直接修改源分支源分支仅做同步 ContiNew 最新代码用)
4. 在新分支上进行代码修改完成后提交并 push 到您的远程仓库
5.开源平台上创建 pull request (PR),选择正确的源分支和目标分支,按模板填写说明信息参考 [已合并的 PR](https://github.com/continew-org/continew-admin-ui/pulls?q=is%3Apr+is%3Amerged) 可提高合并率)
6. 提交 PR 后,系统会提示签署 CLA贡献者协议)请确保 commit 使用的邮箱与平台绑定邮箱一致(如果不一致,可以在本地通过 `git reset --soft HEAD~1` 回退,然后使用正确邮箱重新提交,最后 `git push -f` 即可,不需要重新创建 PR然后使用该邮箱签署即可
7. 耐心等待维护者审核并合并您的 PR建议通过交流群进行快捷沟通
8. PR 合并后,下次贡献前请先同步最新代码,再重复步骤 3 开始
> [!IMPORTANT]
> 欢迎大家为 ContiNew Admin 贡献代码,我们非常感谢您的支持!为了更好地管理项目,维护者有一些要求
> 为了确保项目质量和协作效率,请注意以下事项
>
> 1. 请确保代码配置文件的结构命名规范良好,完善的代码注释甚至包括接口文档参数示例,并遵循阿里巴巴 <a href="https://github.com/continew-org/continew-admin/blob/dev/.style/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf" target="_blank">《Java开发手册(黄山版)》</a> 中的代码规范,保证代码质量和可维护性
> 2. 提交代码前,请按照 [Angular 提交规范](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular) 编写 commit 的 message建议在 IntelliJ IDEA 中下载并安装 Git Commit Template 插件,以便按照规范进行 commit
> 3. 提交代码之前,请关闭所有代码窗口,执行 `mvn compile` 命令(代码格式化插件会在项目编译时对全局代码进行格式修正),编译通过后,不要再打开查看任何代码窗口,直接提交即可,以免不同的 IDE 配置会自动进行代码格式化
> 1. 代码配置文件请参考已有风格,遵循清晰的结构命名规范,提供完善的注释(包括接口文档参数示例),后端代码请符合阿里巴巴 <a href="https://github.com/continew-org/continew-admin/blob/dev/.style/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf" target="_blank">《Java开发手册(黄山版)》</a> 中的代码规范
> 2. 提交后端代码前请关闭所有代码窗口,执行 `mvn compile` 命令进行代码格式化ContiNew 项目后端编译时会自动执行插件进行代码格式修正)。编译通过后请勿再次打开代码窗口,避免不同 IDE 配置导致的格式差异
> 3. 提交时,请按照 [Angular 提交规范](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular) 编写 commit message参考已有风格
## 反馈交流

View File

@@ -32,7 +32,8 @@ public interface MenuApi {
* 查询树结构列表
*
* @param excludeMenuIds 排除的菜单 ID 列表
* @param isSimple 是否是简单树结构
* @return 树结构列表
*/
List<Tree<Long>> listTree(List<Long> excludeMenuIds);
List<Tree<Long>> listTree(List<Long> excludeMenuIds, boolean isSimple);
}

View File

@@ -68,5 +68,5 @@ export function export${classNamePrefix}(query: ${classNamePrefix}Query) {
/** @desc 查询${businessName}字典 */
export function list${classNamePrefix}Dict(query?: ${classNamePrefix}Query) {
return http.get<LabelValueState[]>(`${BASE_URL}/dict`, query)
return http.get<LabelValueState[]>(`${'$'}{BASE_URL}/dict`, query)
}

View File

@@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.continew.admin.common.api.system.MenuApi;
import top.continew.admin.common.base.controller.BaseController;
@@ -56,7 +57,7 @@ public class PackageController extends BaseController<PackageService, PackageRes
@Operation(summary = "查询租户套餐菜单", description = "查询租户套餐菜单树列表")
@SaCheckPermission("tenant:package:list")
@GetMapping("/menu/tree")
public List<Tree<Long>> listMenuTree() {
return menuApi.listTree(tenantExtensionProperties.getIgnoreMenus());
public List<Tree<Long>> listMenuTree(@RequestParam(required = false, defaultValue = "true") Boolean isSimple) {
return menuApi.listTree(tenantExtensionProperties.getIgnoreMenus(), isSimple);
}
}

View File

@@ -45,6 +45,20 @@ public class TenantQuery implements Serializable {
@Query(columns = {"name", "description"}, type = QueryType.LIKE)
private String description;
/**
* 编码
*/
@Schema(description = "编码", example = "T0stxiJK6RMH")
@Query(type = QueryType.EQ)
private String code;
/**
* 域名
*/
@Schema(description = "域名", example = "admin.continew.top")
@Query(type = QueryType.LIKE)
private String domain;
/**
* 套餐 ID
*/

View File

@@ -47,6 +47,7 @@ import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import java.util.*;
import java.util.stream.Stream;
/**
* Sa-Token 配置
@@ -110,7 +111,7 @@ public class SaTokenConfiguration {
@EventListener(ApplicationReadyEvent.class)
public void configureSaTokenExcludes() {
String[] beanNames = applicationContext.getBeanDefinitionNames();
List<String> additionalExcludes = Arrays.stream(beanNames).parallel().map(beanName -> {
List<String> additionalExcludes = Arrays.stream(beanNames).map(beanName -> {
Object bean = applicationContext.getBean(beanName);
Class<?> clazz = bean.getClass();
if (AopUtils.isAopProxy(bean)) {
@@ -130,10 +131,10 @@ public class SaTokenConfiguration {
}).filter(Objects::nonNull).toList();
if (!additionalExcludes.isEmpty()) {
// 合并现有的 excludes 和新扫描到的
List<String> allExcludes = new ArrayList<>(Arrays.asList(properties.getSecurity().getExcludes()));
allExcludes.addAll(additionalExcludes);
// 转回数组
properties.getSecurity().setExcludes(allExcludes.toArray(new String[0]));
String[] existingExcludes = Optional.ofNullable(properties.getSecurity().getExcludes()).orElse(new String[0]);
String[] combinedExcludes = Stream.concat(Arrays.stream(existingExcludes), additionalExcludes.stream())
.toArray(String[]::new);
properties.getSecurity().setExcludes(combinedExcludes);
}
log.debug("缓存 CRUD API 权限前缀完成:{}", CrudApiPermissionPrefixCache.getAll().values());
}

View File

@@ -39,11 +39,11 @@ public class MenuApiImpl implements MenuApi {
private final MenuService baseService;
@Override
public List<Tree<Long>> listTree(List<Long> excludeMenuIds) {
public List<Tree<Long>> listTree(List<Long> excludeMenuIds, boolean isSimple) {
MenuQuery query = new MenuQuery();
query.setStatus(DisEnableStatusEnum.ENABLE);
// 过滤掉租户不能使用的菜单
query.setExcludeMenuIdList(excludeMenuIds);
return baseService.tree(query, null, true);
return baseService.tree(query, null, isSimple);
}
}

View File

@@ -60,7 +60,7 @@ public class FileController extends BaseController<FileService, FileResp, FileRe
/**
* 上传文件
* <p>
* 公共上传文件请使用 {@link top.continew.admin.controller.common.CommonController#upload}
* 公共上传文件请使用 {@link CommonController#upload}
* </p>
*
* @param file 文件
@@ -69,7 +69,7 @@ public class FileController extends BaseController<FileService, FileResp, FileRe
* @throws IOException /
*/
@Operation(summary = "上传文件", description = "上传文件")
@Parameter(name = "parentPath", description = "上级目录", example = "/", in = ParameterIn.QUERY)
@Parameter(name = "parentPath", description = "上级目录(默认:/yyyy/MM/dd", example = "/", in = ParameterIn.QUERY)
@SaCheckPermission("system:file:upload")
@PostMapping("/upload")
public FileUploadResp upload(@NotNull(message = "文件不能为空") @RequestPart MultipartFile file,

View File

@@ -16,6 +16,7 @@
package top.continew.admin.system.service;
import cn.hutool.core.util.StrUtil;
import org.dromara.x.file.storage.core.FileInfo;
import org.springframework.web.multipart.MultipartFile;
import top.continew.admin.common.base.service.BaseService;
@@ -60,7 +61,7 @@ public interface FileService extends BaseService<FileResp, FileResp, FileQuery,
* @throws IOException /
*/
default FileInfo upload(MultipartFile file, String parentPath) throws IOException {
return upload(file, parentPath, null);
return upload(file, StrUtil.blankToDefault(parentPath, getDefaultParentPath()), null);
}
/**
@@ -94,7 +95,7 @@ public interface FileService extends BaseService<FileResp, FileResp, FileQuery,
* @throws IOException /
*/
default FileInfo upload(File file, String parentPath) throws IOException {
return upload(file, parentPath, null);
return upload(file, StrUtil.blankToDefault(parentPath, getDefaultParentPath()), null);
}
/**

View File

@@ -252,10 +252,9 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
* 处理路径
*
* <p>
* 1.如果 path 为空,则使用 {@link FileService#getDefaultParentPath()} 作为默认值 <br />
* 2.如果 path {@code /},则设置为空 <br />
* 3.如果 path 以 {@code /} 结尾,则添加后缀 {@code /} <br />
* 4.如果 path 以 {@code /} 开头,则移除前缀 {@code /} <br />
* 1.如果 path 为 {@code /},则设置为空 <br />
* 2.如果 path 不以 {@code /} 结尾,则添加后缀 {@code /} <br />
* 3.如果 path 以 {@code /} 开头,则移除前缀 {@code /} <br />
* 示例yyyy/MM/dd/
* </p>
*
@@ -263,9 +262,6 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
* @return 处理路径
*/
private String pretreatmentPath(String path) {
if (StrUtil.isBlank(path)) {
return this.getDefaultParentPath();
}
if (StringConstants.SLASH.equals(path)) {
return StringConstants.EMPTY;
}

View File

@@ -32,16 +32,19 @@ import top.continew.admin.common.enums.RoleCodeEnum;
import top.continew.admin.system.constant.SystemConstants;
import top.continew.admin.system.mapper.UserRoleMapper;
import top.continew.admin.system.model.entity.UserRoleDO;
import top.continew.admin.system.model.entity.user.UserDO;
import top.continew.admin.system.model.query.RoleUserQuery;
import top.continew.admin.system.model.resp.role.RoleUserResp;
import top.continew.admin.system.service.RoleService;
import top.continew.admin.system.service.UserRoleService;
import top.continew.admin.system.service.UserService;
import top.continew.starter.core.util.CollUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.data.util.QueryWrapperHelper;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.resp.PageResp;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -59,6 +62,9 @@ public class UserRoleServiceImpl implements UserRoleService {
@Lazy
@Resource
private RoleService roleService;
@Lazy
@Resource
private UserService userService;
@Override
@AutoOperate(type = RoleUserResp.class, on = "list")
@@ -79,6 +85,11 @@ public class UserRoleServiceImpl implements UserRoleService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean assignRolesToUser(List<Long> roleIds, Long userId) {
UserDO userDO = userService.getById(userId);
if (Boolean.TRUE.equals(userDO.getIsSystem())) {
Collection<Long> disjunctionRoleIds = CollUtil.disjunction(roleIds, this.listRoleIdByUserId(userId));
CheckUtils.throwIfNotEmpty(disjunctionRoleIds, "[{}] 是系统内置用户,不允许变更角色", userDO.getNickname());
}
// 超级管理员和租户管理员角色不允许分配
CheckUtils.throwIf(roleIds.contains(SystemConstants.SUPER_ADMIN_ROLE_ID), "不允许分配超级管理员角色");
Set<String> roleCodeSet = CollUtils.mapToSet(roleService.listByUserId(userId), RoleContext::getCode);