mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-14 03:01:36 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
302f21867a | |||
403c72aa52 | |||
c87317d199 | |||
098571ffb2 | |||
89031954c0 | |||
6324a5af88 | |||
387fb19464 | |||
ca9f34d3d5 | |||
ad9a6000fc | |||
25240fa819 | |||
51abd7e802 | |||
f5ee2b5beb | |||
bd7bf43134 | |||
ecc98b1999 | |||
bb5a92e5ca | |||
6c4e2522df | |||
fdd21a01c1 | |||
e17d5db0fb | |||
9e91f563e2 | |||
d56b9aa35e | |||
0f1479f40d | |||
62d99b8a4e | |||
75cef77318 | |||
2c1eb5660f | |||
d7621c6b26 |
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,3 +1,40 @@
|
||||
## [v3.3.0](https://github.com/continew-org/continew-admin/compare/v3.2.0...v3.3.0) (2024-09-09)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
* 重构全局响应处理及异常拦截,自定义异常拦截从 Starter 调整到 Admin 项目 ([d7621c6](https://github.com/continew-org/continew-admin/commit/d7621c6b26bfd253d9295444ea144f5dabf67f44))
|
||||
* 重构 Controller 接口方法返回值写法,接口文档也已适配处理 ([d7621c6](https://github.com/continew-org/continew-admin/commit/d7621c6b26bfd253d9295444ea144f5dabf67f44)) ([0f1479f](https://github.com/continew-org/continew-admin/commit/0f1479f40deef83a5f5d64cbc24a7691b274b112))
|
||||
* 代码生成字段配置时支持指定排序 ([d56b9aa](https://github.com/continew-org/continew-admin/commit/d56b9aa35ee2502804f487487d7bac02f4edc9b0))
|
||||
* 代码生成字段配置时支持选择关联字典 ([fdd21a0](https://github.com/continew-org/continew-admin/commit/fdd21a01c106e12321d0d1886ff643c72a09943b)) ([ecc98b1](https://github.com/continew-org/continew-admin/commit/ecc98b1999d90c1a7a29af94dc8705283f34dada))
|
||||
* 修改角色功能权限、数据权限支持衔接新增角色时的父子联动选项 ([387fb19](https://github.com/continew-org/continew-admin/commit/387fb194640d4a288f053c3eba1bf5b314d64da7))
|
||||
|
||||
### 💎 功能优化
|
||||
|
||||
- 移除 WebMvcConfiguration 配置(已迁移到 Starter 项目)([d7621c6](https://github.com/continew-org/continew-admin/commit/d7621c6b26bfd253d9295444ea144f5dabf67f44))
|
||||
- 重构日志持久层接口本地实现类 ([2c1eb56](https://github.com/continew-org/continew-admin/commit/2c1eb5660f69a9ab702d503944a11e47edac1142))
|
||||
- 优化打包配置,模板等配置文件提取到 jar 包外部 ([75cef77](https://github.com/continew-org/continew-admin/commit/75cef773187e5b5060a10a12e7c9912002376d7a))
|
||||
- 优化健康监测接口响应信息 ([bb5a92e](https://github.com/continew-org/continew-admin/commit/bb5a92e5ca238ed677d9ac3589fdf8009d2ac232))
|
||||
- 优化代码生成列配置代码,取消后端部分默认值 ([f5ee2b5](https://github.com/continew-org/continew-admin/commit/f5ee2b5beb9572d3fcd5b7c2f6db0627dedb31aa)) ([ca9f34d](https://github.com/continew-org/continew-admin/commit/ca9f34d3d5a3f96c6df537036a5fd876cae2e89a))
|
||||
- 重构权限变更逻辑,修改角色、变更用户角色不再下线用户 ([ad9a600](https://github.com/continew-org/continew-admin/commit/ad9a6000fcb5d64b04cf230caa3cbacc8c3ac8d7))
|
||||
|
||||
### 🐛 问题修复
|
||||
|
||||
- 修复打包部署后,下载用户导入模板异常问题 (Gitee#25) ([c7ffc67](https://github.com/continew-org/continew-admin/commit/c7ffc67cdc9139a4398c7dc819ca453880bd100a))
|
||||
- 修复日志记录仅支持获取 JSON 结构响应体的问题 ([d7621c6](https://github.com/continew-org/continew-admin/commit/d7621c6b26bfd253d9295444ea144f5dabf67f44))
|
||||
- 修复并增强 SQL 注入防御 ([0f1479f](https://github.com/continew-org/continew-admin/commit/0f1479f40deef83a5f5d64cbc24a7691b274b112))
|
||||
- 修复目录、菜单的组件名称重复的错误问题 ([9e91f56](https://github.com/continew-org/continew-admin/commit/9e91f563e2a263ce302dc3bf17c89e37c2b56285))
|
||||
- 修复 DataPermission 注解表别名配置无效的问题 ([6c4e252](https://github.com/continew-org/continew-admin/commit/6c4e2522df3f44ba0f5a21228e805b8ac98f8e6b))
|
||||
- 临时移除 MyBatis Plus saveBatch 不兼容的 rewriteBatchedStatements 配置 ([25240fa](https://github.com/continew-org/continew-admin/commit/25240fa81957a1677deda294ce8f2b0af5413315))
|
||||
- 修复更新会导致原加密失效的问题 ([8903195](https://github.com/continew-org/continew-admin/commit/89031954c0b7daee1c08e1a10fd50139301cd6ab)) ([c87317d](https://github.com/continew-org/continew-admin/commit/c87317d19946989e86dfbc5f24b155b2ea5abdc9))
|
||||
- 修复角色查询参数与前端不一致的问题 ([098571f](https://github.com/continew-org/continew-admin/commit/098571ffb2febc6163d2b9e5b18c4796ea80cbfa))
|
||||
- 修复特殊校验异常不打印堆栈 ([c87317d](https://github.com/continew-org/continew-admin/commit/c87317d19946989e86dfbc5f24b155b2ea5abdc9))
|
||||
- 修复日志全局 includes 配置会被局部修改的问题 ([c87317d](https://github.com/continew-org/continew-admin/commit/c87317d19946989e86dfbc5f24b155b2ea5abdc9))
|
||||
- 修复初始数据错误 ([403c72a](https://github.com/continew-org/continew-admin/commit/403c72aa52a0f0852208f15fa1c7117ee26414f0))
|
||||
|
||||
### 📦 依赖升级
|
||||
|
||||
- ContiNew Starter 2.4.0 => 2.6.0 (更多特性及依赖升级详情,请查看 ContiNew Starter [更新日志](https://github.com/continew-org/continew-starter/blob/dev/CHANGELOG.md))
|
||||
|
||||
## [v3.2.0](https://github.com/continew-org/continew-admin/compare/v3.1.0...v3.2.0) (2024-08-05)
|
||||
|
||||
### ✨ 新特性
|
||||
|
22
README.md
22
README.md
@@ -4,7 +4,7 @@
|
||||
<img src="https://img.shields.io/badge/License-Apache--2.0-blue.svg" alt="License" />
|
||||
</a>
|
||||
<a href="https://github.com/continew-org/continew-admin" target="_blank">
|
||||
<img src="https://img.shields.io/badge/RELEASE-v3.2.0-%23ff3f59.svg" alt="Release" />
|
||||
<img src="https://img.shields.io/badge/RELEASE-v3.3.0-%23ff3f59.svg" alt="Release" />
|
||||
</a>
|
||||
<a href="https://app.codacy.com/gh/continew-org/continew-admin/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade" target="_blank">
|
||||
<img src="https://app.codacy.com/project/badge/Grade/19e3e2395d554efe902c3822e65db30e" alt="Codacy Badge" />
|
||||
@@ -13,7 +13,7 @@
|
||||
<img src="https://sonarcloud.io/api/project_badges/measure?project=Charles7c_continew-admin&metric=alert_status" alt="Sonar Status" />
|
||||
</a>
|
||||
<a href="https://github.com/continew-org/continew-starter" target="_blank">
|
||||
<img src="https://img.shields.io/badge/ContiNew Starter-2.4.0-%236CB52D.svg" alt="ContiNew Starter" />
|
||||
<img src="https://img.shields.io/badge/ContiNew Starter-2.6.0-%236CB52D.svg" alt="ContiNew Starter" />
|
||||
</a>
|
||||
<a href="https://spring.io/projects/spring-boot" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Spring Boot-3.2.7-%236CB52D.svg?logo=Spring-Boot" alt="Spring Boot" />
|
||||
@@ -213,13 +213,13 @@ public class DeptController extends BaseController<DeptService, DeptResp, DeptDe
|
||||
| 名称 | 版本 | 简介 |
|
||||
| :----------------------------------------------------------- | :----------- | :----------------------------------------------------------- |
|
||||
| <a href="https://cn.vuejs.org/" target="_blank">Vue</a> | 3.4.21 | 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 |
|
||||
| <a href="https://arco.design/vue/docs/start" target="_blank">Arco Design</a> | 2.55.0 | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。 |
|
||||
| <a href="https://arco.design/vue/docs/start" target="_blank">Arco Design</a> | 2.56.0 | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。 |
|
||||
| <a href="https://www.typescriptlang.org/zh/" target="_blank">TypeScript</a> | 5.0.4 | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 |
|
||||
| <a href="https://cn.vitejs.dev/" target="_blank">Vite</a> | 5.1.5 | 下一代的前端工具链,为开发提供极速响应。 |
|
||||
| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.4.0 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 |
|
||||
| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.6.0 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 |
|
||||
| <a href="https://spring.io/projects/spring-boot" target="_blank">Spring Boot</a> | 3.2.7 | 简化 Spring 应用的初始搭建和开发过程,基于“约定优于配置”的理念,使开发人员不再需要定义样板化的配置。(Spring Boot 3.0 开始,要求 Java 17 作为最低版本) |
|
||||
| <a href="https://undertow.io/" target="_blank">Undertow</a> | 2.3.13.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 |
|
||||
| <a href="https://sa-token.dev33.cn/" target="_blank">Sa-Token + JWT</a> | 1.38.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 |
|
||||
| <a href="https://sa-token.dev33.cn/" target="_blank">Sa-Token + JWT</a> | 1.39.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 |
|
||||
| <a href="https://baomidou.com/" target="_blank">MyBatis Plus</a> | 3.5.7 | MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率。 |
|
||||
| <a href="https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611" target="_blank">dynamic-datasource-spring-boot-starter</a> | 4.3.1 | 基于 Spring Boot 的快速集成多数据源的启动器。 |
|
||||
| Hikari | 5.0.1 | JDBC 连接池,号称 “史上最快连接池”,SpringBoot 在 2.0 之后,采用的默认数据库连接池就是 Hikari。 |
|
||||
@@ -228,18 +228,20 @@ public class DeptController extends BaseController<DeptService, DeptResp, DeptDe
|
||||
| <a href="https://github.com/p6spy/p6spy" target="_blank">P6Spy</a> | 3.9.1 | SQL 性能分析组件。 |
|
||||
| <a href="https://github.com/liquibase/liquibase" target="_blank">Liquibase</a> | 4.24.0 | 用于管理数据库版本,跟踪、管理和应用数据库变化。 |
|
||||
| [JetCache](https://github.com/alibaba/jetcache/blob/master/docs/CN/Readme.md) | 2.7.6 | 一个基于 Java 的缓存系统封装,提供统一的 API 和注解来简化缓存的使用。提供了比 SpringCache 更加强大的注解,可以原生的支持 TTL、两级缓存、分布式自动刷新,还提供了 Cache 接口用于手工缓存操作。 |
|
||||
| <a href="https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D" target="_blank">Redisson</a> | 3.32.0 | 不仅仅是一个 Redis Java 客户端,Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,为使用者提供了一系列具有分布式特性的常用工具:分布式锁、限流器等。 |
|
||||
| <a href="https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D" target="_blank">Redisson</a> | 3.35.0 | 不仅仅是一个 Redis Java 客户端,Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,为使用者提供了一系列具有分布式特性的常用工具:分布式锁、限流器等。 |
|
||||
| <a href="https://redis.io/" target="_blank">Redis</a> | 7.2.3 | 高性能的 key-value 数据库。 |
|
||||
| [Snail Job](https://snailjob.opensnail.com/) | 1.1.0 | 灵活,可靠和快速的分布式任务重试和分布式任务调度平台。 |
|
||||
| [X File Storage](https://x-file-storage.xuyanwu.cn/#/) | 2.2.0 | 一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS...等其它兼容 S3 协议的存储平台。 |
|
||||
| <a href="https://sms4j.com/" target="_blank">SMS4J</a> | 3.2.1 | 短信聚合框架,轻松集成多家短信服务,解决接入多个短信 SDK 的繁琐流程。 |
|
||||
| [Snail Job](https://snailjob.opensnail.com/) | 1.1.2 | 灵活,可靠和快速的分布式任务重试和分布式任务调度平台。 |
|
||||
| [X File Storage](https://x-file-storage.xuyanwu.cn/#/) | 2.2.1 | 一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS...等其它兼容 S3 协议的存储平台。 |
|
||||
| <a href="https://sms4j.com/" target="_blank">SMS4J</a> | 3.3.2 | 短信聚合框架,轻松集成多家短信服务,解决接入多个短信 SDK 的繁琐流程。 |
|
||||
| <a href="https://justauth.cn/" target="_blank">Just Auth</a> | 1.16.6 | 开箱即用的整合第三方登录的开源组件,脱离繁琐的第三方登录 SDK,让登录变得 So easy! |
|
||||
| <a href="https://easyexcel.opensource.alibaba.com/" target="_blank">Easy Excel</a> | 4.0.1 | 一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。 |
|
||||
| [AJ-Captcha](https://ajcaptcha.beliefteam.cn/captcha-doc/) | 1.3.0 | Java 行为验证码,包含滑动拼图、文字点选两种方式,UI支持弹出和嵌入两种方式。 |
|
||||
| Easy Captcha | 1.6.2 | Java 图形验证码,支持 gif、中文、算术等类型,可用于 Java Web、JavaSE 等项目。 |
|
||||
| [Crane4j](https://createsequence.gitee.io/crane4j-doc/#/) | 2.9.0 | 一个基于注解的,用于完成一切 “根据 A 的 key 值拿到 B,再把 B 的属性映射到 A” 这类需求的字段填充框架。 |
|
||||
| [CosID](https://cosid.ahoo.me/guide/getting-started.html) | 2.9.6 | 旨在提供通用、灵活、高性能的分布式 ID 生成器。 |
|
||||
| [Graceful Response](https://doc.feiniaojin.com/graceful-response/home.html) | 5.0.0-boot3 | 一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量。 |
|
||||
| <a href="https://doc.xiaominfo.com/" target="_blank">Knife4j</a> | 4.5.0 | 前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案。 |
|
||||
| <a href="https://www.hutool.cn/" target="_blank">Hutool</a> | 5.8.29 | 小而全的 Java 工具类库,通过静态方法封装,降低相关 API 的学习成本,提高工作效率,使 Java 拥有函数式语言般的优雅,让 Java 语言也可以“甜甜的”。 |
|
||||
| <a href="https://www.hutool.cn/" target="_blank">Hutool</a> | 5.8.32 | 小而全的 Java 工具类库,通过静态方法封装,降低相关 API 的学习成本,提高工作效率,使 Java 拥有函数式语言般的优雅,让 Java 语言也可以“甜甜的”。 |
|
||||
| <a href="https://projectlombok.org/" target="_blank">Lombok</a> | 1.18.32 | 在 Java 开发过程中用注解的方式,简化了 JavaBean 的编写,避免了冗余和样板式代码,让编写的类更加简洁。 |
|
||||
|
||||
## 快速开始
|
||||
|
@@ -67,7 +67,7 @@
|
||||
<!-- ContiNew Starter 数据访问模块 - MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-mybatis-plus</artifactId>
|
||||
<artifactId>continew-starter-data-mp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ContiNew Starter 缓存模块 - JetCache -->
|
||||
|
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.common.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Web MVC 配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2022/12/11 19:40
|
||||
*/
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
|
||||
|
||||
/**
|
||||
* 解决 Jackson2ObjectMapperBuilderCustomizer 配置不生效的问题
|
||||
* <p>
|
||||
* MappingJackson2HttpMessageConverter 对象在程序启动时创建了多个,移除多余的,保证只有一个
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
|
||||
if (Objects.isNull(mappingJackson2HttpMessageConverter)) {
|
||||
converters.add(0, new MappingJackson2HttpMessageConverter());
|
||||
} else {
|
||||
converters.add(0, mappingJackson2HttpMessageConverter);
|
||||
}
|
||||
// 自定义 converters 时,需要手动在最前面添加 ByteArrayHttpMessageConverter
|
||||
// 否则 Spring Doc OpenAPI 的 /*/api-docs/**(例如:/v3/api-docs/default)接口响应内容会变为 Base64 编码后的内容,最终导致接口文档解析失败
|
||||
// 详情请参阅:https://github.com/springdoc/springdoc-openapi/issues/2143
|
||||
converters.add(0, new ByteArrayHttpMessageConverter());
|
||||
}
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.common.config.exception;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import top.continew.starter.core.exception.BadRequestException;
|
||||
import top.continew.starter.core.exception.BusinessException;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2024/8/7 20:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Order(99)
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 拦截业务异常
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public R handleBusinessException(BusinessException e, HttpServletRequest request) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
return R.fail(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截自定义验证异常-错误请求
|
||||
*/
|
||||
@ExceptionHandler(BadRequestException.class)
|
||||
public R handleBadRequestException(BadRequestException e, HttpServletRequest request) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截文件上传异常-超过上传大小限制
|
||||
*/
|
||||
@ExceptionHandler(MultipartException.class)
|
||||
public R handleRequestTooBigException(MultipartException e, HttpServletRequest request) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
String msg = e.getMessage();
|
||||
R defaultFail = R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), msg);
|
||||
if (CharSequenceUtil.isBlank(msg)) {
|
||||
return defaultFail;
|
||||
}
|
||||
String sizeLimit;
|
||||
Throwable cause = e.getCause();
|
||||
if (null != cause) {
|
||||
msg = msg.concat(cause.getMessage().toLowerCase());
|
||||
}
|
||||
if (msg.contains("size") && msg.contains("exceed")) {
|
||||
sizeLimit = CharSequenceUtil.subBetween(msg, "the maximum size ", " for");
|
||||
} else if (msg.contains("larger than")) {
|
||||
sizeLimit = CharSequenceUtil.subAfter(msg, "larger than ", true);
|
||||
} else {
|
||||
return defaultFail;
|
||||
}
|
||||
String errorMsg = "请上传小于 %sKB 的文件".formatted(NumberUtil.parseLong(sizeLimit) / 1024);
|
||||
return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMsg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.common.config.exception;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 全局 SaToken 异常处理器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2024/8/7 20:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Order(99)
|
||||
@RestControllerAdvice
|
||||
public class GlobalSaTokenExceptionHandler {
|
||||
|
||||
/**
|
||||
* 认证异常-登录认证
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public R handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
String errorMsg = switch (e.getType()) {
|
||||
case NotLoginException.KICK_OUT -> "您已被踢下线";
|
||||
case NotLoginException.BE_REPLACED_MESSAGE -> "您已被顶下线";
|
||||
default -> "您的登录状态已过期,请重新登录";
|
||||
};
|
||||
return R.fail(String.valueOf(HttpStatus.UNAUTHORIZED.value()), errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证异常-权限认证
|
||||
*/
|
||||
@ExceptionHandler(NotPermissionException.class)
|
||||
public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证异常-角色认证
|
||||
*/
|
||||
@ExceptionHandler(NotRoleException.class)
|
||||
public R handleNotRoleException(NotRoleException e, HttpServletRequest request) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权");
|
||||
}
|
||||
}
|
@@ -19,9 +19,9 @@ package top.continew.admin.common.config.mybatis;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import top.continew.admin.common.model.dto.LoginUser;
|
||||
import top.continew.admin.common.util.helper.LoginHelper;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataPermissionCurrentUser;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataPermissionFilter;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataScope;
|
||||
import top.continew.starter.data.mp.datapermission.DataPermissionCurrentUser;
|
||||
import top.continew.starter.data.mp.datapermission.DataPermissionFilter;
|
||||
import top.continew.starter.data.mp.datapermission.DataScope;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@@ -20,8 +20,8 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataPermission;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.datapermission.DataPermission;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -20,7 +20,7 @@ import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataPermissionFilter;
|
||||
import top.continew.starter.data.mp.datapermission.DataPermissionFilter;
|
||||
|
||||
/**
|
||||
* MyBatis Plus 配置
|
||||
|
@@ -25,6 +25,7 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
@@ -109,16 +110,17 @@ public class LoginUser implements Serializable {
|
||||
*/
|
||||
private Integer passwordExpirationDays;
|
||||
|
||||
public LoginUser(Set<String> permissions,
|
||||
Set<String> roleCodes,
|
||||
Set<RoleDTO> roles,
|
||||
Integer passwordExpirationDays) {
|
||||
public LoginUser(Set<String> permissions, Set<RoleDTO> roles, Integer passwordExpirationDays) {
|
||||
this.permissions = permissions;
|
||||
this.roleCodes = roleCodes;
|
||||
this.roles = roles;
|
||||
this.setRoles(roles);
|
||||
this.passwordExpirationDays = passwordExpirationDays;
|
||||
}
|
||||
|
||||
public void setRoles(Set<RoleDTO> roles) {
|
||||
this.roles = roles;
|
||||
this.roleCodes = roles.stream().map(RoleDTO::getCode).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
|
@@ -69,6 +69,18 @@ public class LoginHelper {
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新登录用户信息
|
||||
*
|
||||
* @param loginUser
|
||||
* 登录用户信息
|
||||
* @param token 令牌
|
||||
*/
|
||||
public static void updateLoginUser(LoginUser loginUser, String token) {
|
||||
SaHolder.getStorage().delete(CacheConstants.LOGIN_USER_KEY);
|
||||
StpUtil.getTokenSessionByToken(token).set(CacheConstants.LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录用户信息
|
||||
*
|
||||
|
@@ -14,7 +14,7 @@
|
||||
|
||||
<properties>
|
||||
<!-- SnailJob 服务端 -->
|
||||
<snail-job.version>1.1.0</snail-job.version>
|
||||
<snail-job.version>1.1.2</snail-job.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -19,7 +19,7 @@ package top.continew.admin.generator.mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.generator.mapper;
|
||||
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 生成配置 Mapper
|
||||
|
@@ -141,6 +141,12 @@ public class FieldConfigDO implements Serializable {
|
||||
@Schema(description = "查询方式", example = "1")
|
||||
private QueryTypeEnum queryType;
|
||||
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
@Schema(description = "字典编码", example = "notice_type")
|
||||
private String dictCode;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@@ -151,19 +157,22 @@ public class FieldConfigDO implements Serializable {
|
||||
public FieldConfigDO(@NonNull Column column) {
|
||||
this.setTableName(column.getTableName());
|
||||
this.setColumnName(column.getName());
|
||||
this.setColumnType(StrUtil.splitToArray(column.getTypeName(), StringConstants.SPACE)[0].toLowerCase());
|
||||
this.setColumnType(column.getTypeName());
|
||||
this.setColumnSize(Convert.toStr(column.getSize()));
|
||||
this.setComment(column.getComment());
|
||||
this.setIsRequired(!column.isPk() && !column.isNullable());
|
||||
this.setShowInList(true);
|
||||
this.setShowInForm(this.getIsRequired());
|
||||
this.setShowInQuery(this.getIsRequired());
|
||||
this.setFormType(FormTypeEnum.INPUT);
|
||||
this.setQueryType("String".equals(this.getFieldType()) ? QueryTypeEnum.LIKE : QueryTypeEnum.EQ);
|
||||
}
|
||||
|
||||
public void setColumnName(String columnName) {
|
||||
this.columnName = columnName;
|
||||
this.fieldName = StrUtil.toCamelCase(this.columnName);
|
||||
}
|
||||
|
||||
public void setColumnType(String columnType) {
|
||||
String[] arr = StrUtil.splitToArray(columnType, StringConstants.SPACE);
|
||||
this.columnType = arr.length > 1 ? arr[0].toLowerCase() : columnType.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
@@ -18,18 +18,15 @@ package top.continew.admin.generator.model.entity;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import top.continew.admin.common.constant.RegexConstants;
|
||||
import top.continew.starter.core.util.StrUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
@@ -118,21 +115,16 @@ public class GenConfigDO implements Serializable {
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 类名前缀
|
||||
*/
|
||||
@Setter(AccessLevel.NONE)
|
||||
@JsonIgnore
|
||||
@TableField(exist = false)
|
||||
private String classNamePrefix;
|
||||
|
||||
public GenConfigDO(String tableName) {
|
||||
this.tableName = tableName;
|
||||
this.setTableName(tableName);
|
||||
}
|
||||
|
||||
public String getClassNamePrefix() {
|
||||
String rawClassName = StrUtils.blankToDefault(this.tablePrefix, this.tableName, prefix -> StrUtil
|
||||
.removePrefix(this.tableName, prefix));
|
||||
return StrUtil.upperFirst(StrUtil.toCamelCase(rawClassName));
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
// 默认表前缀(sys_user -> sys_)
|
||||
int underLineIndex = StrUtil.indexOf(tableName, StringConstants.C_UNDERLINE);
|
||||
if (-1 != underLineIndex) {
|
||||
this.tablePrefix = StrUtil.subPre(tableName, underLineIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.entity;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.StrUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 内部生成配置信息
|
||||
*
|
||||
* @author zhangqcc
|
||||
* @since 2024/8/30 19:35
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class InnerGenConfigDO extends GenConfigDO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 字段配置信息
|
||||
*/
|
||||
private List<FieldConfigDO> fieldConfigs;
|
||||
|
||||
/**
|
||||
* 生成时间
|
||||
*/
|
||||
private String datetime;
|
||||
|
||||
/**
|
||||
* API 模块名称
|
||||
*/
|
||||
private String apiModuleName;
|
||||
|
||||
/**
|
||||
* API 名称
|
||||
*/
|
||||
private String apiName;
|
||||
|
||||
/**
|
||||
* 类名
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 类名前缀
|
||||
*/
|
||||
private String classNamePrefix;
|
||||
|
||||
/**
|
||||
* 子包名称
|
||||
*/
|
||||
private String subPackageName;
|
||||
|
||||
/**
|
||||
* 字典编码列表
|
||||
*/
|
||||
private Set<String> dictCodes;
|
||||
|
||||
/**
|
||||
* 是否包含必填字段
|
||||
*/
|
||||
private boolean hasRequiredField;
|
||||
|
||||
/**
|
||||
* 是否包含字典字段
|
||||
*/
|
||||
private boolean hasDictField;
|
||||
|
||||
/**
|
||||
* 是否包含 BigDecimal 字段
|
||||
*/
|
||||
private boolean hasBigDecimalField;
|
||||
|
||||
/**
|
||||
* 是否包含 List 字段
|
||||
*/
|
||||
private boolean hasListField;
|
||||
|
||||
/**
|
||||
* 是否包含 Time 包字段
|
||||
*/
|
||||
private boolean hasTimeField;
|
||||
|
||||
public InnerGenConfigDO() {
|
||||
}
|
||||
|
||||
public InnerGenConfigDO(GenConfigDO genConfig) {
|
||||
BeanUtil.copyProperties(genConfig, this);
|
||||
this.setDatetime(DateUtil.date().toString("yyyy/MM/dd HH:mm"));
|
||||
this.setApiName(StrUtil.lowerFirst(this.getClassNamePrefix()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPackageName(String packageName) {
|
||||
super.setPackageName(packageName);
|
||||
String realPackageName = this.getPackageName();
|
||||
this.setApiModuleName(StrUtil.subSuf(realPackageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(realPackageName, StringConstants.DOT) + 1));
|
||||
}
|
||||
|
||||
public String getClassNamePrefix() {
|
||||
String tableName = super.getTableName();
|
||||
String rawClassName = StrUtils.blankToDefault(super.getTablePrefix(), tableName, prefix -> StrUtil
|
||||
.removePrefix(tableName, prefix));
|
||||
return StrUtil.upperFirst(StrUtil.toCamelCase(rawClassName));
|
||||
}
|
||||
}
|
@@ -16,7 +16,6 @@
|
||||
|
||||
package top.continew.admin.generator.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
@@ -86,8 +85,7 @@ public interface GeneratorService {
|
||||
* 生成代码
|
||||
*
|
||||
* @param tableNames 表明层
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
*/
|
||||
void generate(List<String> tableNames, HttpServletRequest request, HttpServletResponse response);
|
||||
void generate(List<String> tableNames, HttpServletResponse response);
|
||||
}
|
||||
|
@@ -19,26 +19,27 @@ package top.continew.admin.generator.service.impl;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import cn.hutool.db.meta.Column;
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.generator.config.properties.GeneratorProperties;
|
||||
import top.continew.admin.generator.enums.FormTypeEnum;
|
||||
import top.continew.admin.generator.enums.QueryTypeEnum;
|
||||
import top.continew.admin.generator.mapper.FieldConfigMapper;
|
||||
import top.continew.admin.generator.mapper.GenConfigMapper;
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
import top.continew.admin.generator.model.entity.InnerGenConfigDO;
|
||||
import top.continew.admin.generator.model.query.TableQuery;
|
||||
import top.continew.admin.generator.model.req.GenConfigReq;
|
||||
import top.continew.admin.generator.model.resp.GeneratePreviewResp;
|
||||
@@ -79,6 +80,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
private final ProjectProperties projectProperties;
|
||||
private final FieldConfigMapper fieldConfigMapper;
|
||||
private final GenConfigMapper genConfigMapper;
|
||||
private static final List<String> TIME_PACKAGE_CLASS = Arrays.asList("LocalDate", "LocalTime", "LocalDateTime");
|
||||
|
||||
@Override
|
||||
public PageResp<TableResp> pageTable(TableQuery query, PageQuery pageQuery) throws SQLException {
|
||||
@@ -123,11 +125,6 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
if (null != lastGenConfig) {
|
||||
genConfig.setAuthor(lastGenConfig.getAuthor());
|
||||
}
|
||||
// 默认表前缀(sys_user -> sys_)
|
||||
int underLineIndex = StrUtil.indexOf(tableName, StringConstants.C_UNDERLINE);
|
||||
if (-1 != underLineIndex) {
|
||||
genConfig.setTablePrefix(StrUtil.subPre(tableName, underLineIndex + 1));
|
||||
}
|
||||
}
|
||||
return genConfig;
|
||||
}
|
||||
@@ -155,8 +152,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
.orElseGet(() -> new FieldConfigDO(column));
|
||||
// 更新已有字段配置
|
||||
if (null != fieldConfig.getCreateTime()) {
|
||||
String columnType = StrUtil.splitToArray(column.getTypeName(), StringConstants.SPACE)[0].toLowerCase();
|
||||
fieldConfig.setColumnType(columnType);
|
||||
fieldConfig.setColumnType(column.getTypeName());
|
||||
fieldConfig.setColumnSize(Convert.toStr(column.getSize()));
|
||||
}
|
||||
String fieldType = typeMappingEntrySet.stream()
|
||||
@@ -174,19 +170,22 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveConfig(GenConfigReq req, String tableName) {
|
||||
// 保存字段配置
|
||||
// 保存字段配置(先删除再保存)
|
||||
fieldConfigMapper.delete(Wrappers.lambdaQuery(FieldConfigDO.class).eq(FieldConfigDO::getTableName, tableName));
|
||||
List<FieldConfigDO> fieldConfigList = req.getFieldConfigs();
|
||||
for (FieldConfigDO fieldConfig : fieldConfigList) {
|
||||
for (int i = 0; i < fieldConfigList.size(); i++) {
|
||||
FieldConfigDO fieldConfig = fieldConfigList.get(i);
|
||||
// 重新设置排序
|
||||
fieldConfig.setFieldSort(i + 1);
|
||||
if (Boolean.TRUE.equals(fieldConfig.getShowInForm())) {
|
||||
CheckUtils.throwIfNull(fieldConfig.getFormType(), "字段 [{}] 的表单类型不能为空", fieldConfig.getFieldName());
|
||||
fieldConfig.setFormType(ObjectUtil.defaultIfNull(fieldConfig.getFormType(), FormTypeEnum.INPUT));
|
||||
} else {
|
||||
// 在表单中不显示,不需要设置必填
|
||||
fieldConfig.setIsRequired(false);
|
||||
}
|
||||
if (Boolean.TRUE.equals(fieldConfig.getShowInQuery())) {
|
||||
CheckUtils.throwIfNull(fieldConfig.getFormType(), "字段 [{}] 的表单类型不能为空", fieldConfig.getFieldName());
|
||||
CheckUtils.throwIfNull(fieldConfig.getQueryType(), "字段 [{}] 的查询方式不能为空", fieldConfig.getFieldName());
|
||||
fieldConfig.setFormType(ObjectUtil.defaultIfNull(fieldConfig.getFormType(), FormTypeEnum.INPUT));
|
||||
fieldConfig.setQueryType(ObjectUtil.defaultIfNull(fieldConfig.getQueryType(), QueryTypeEnum.EQ));
|
||||
} else {
|
||||
// 在查询中不显示,不需要设置查询方式
|
||||
fieldConfig.setQueryType(null);
|
||||
@@ -218,51 +217,50 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
CheckUtils.throwIfNull(genConfig, "请先进行数据表 [{}] 生成配置", tableName);
|
||||
List<FieldConfigDO> fieldConfigList = fieldConfigMapper.selectListByTableName(tableName);
|
||||
CheckUtils.throwIfEmpty(fieldConfigList, "请先进行数据表 [{}] 字段配置", tableName);
|
||||
Map<String, Object> genConfigMap = BeanUtil.beanToMap(genConfig);
|
||||
genConfigMap.put("date", DateUtil.date().toString("yyyy/MM/dd HH:mm"));
|
||||
String packageName = genConfig.getPackageName();
|
||||
String apiModuleName = StrUtil.subSuf(packageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
genConfigMap.put("apiModuleName", apiModuleName);
|
||||
genConfigMap.put("apiName", StrUtil.lowerFirst(genConfig.getClassNamePrefix()));
|
||||
InnerGenConfigDO innerGenConfig = new InnerGenConfigDO(genConfig);
|
||||
// 渲染代码
|
||||
String classNamePrefix = genConfig.getClassNamePrefix();
|
||||
String classNamePrefix = innerGenConfig.getClassNamePrefix();
|
||||
Map<String, GeneratorProperties.TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
|
||||
for (Map.Entry<String, GeneratorProperties.TemplateConfig> templateConfigEntry : templateConfigMap.entrySet()) {
|
||||
this.pretreatment(genConfigMap, fieldConfigList, templateConfigEntry);
|
||||
String className = classNamePrefix + StrUtil.nullToEmpty(templateConfigEntry.getKey());
|
||||
genConfigMap.put("className", className);
|
||||
GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
|
||||
// 移除需要忽略的字段
|
||||
innerGenConfig.setFieldConfigs(fieldConfigList.stream()
|
||||
.filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig
|
||||
.getExcludeFields()))
|
||||
.toList());
|
||||
// 预处理配置
|
||||
this.pretreatment(innerGenConfig);
|
||||
// 处理其他配置
|
||||
innerGenConfig.setSubPackageName(templateConfig.getPackageName());
|
||||
String classNameSuffix = templateConfigEntry.getKey();
|
||||
String className = classNamePrefix + classNameSuffix;
|
||||
innerGenConfig.setClassName(className);
|
||||
boolean isBackend = templateConfig.isBackend();
|
||||
String extension = templateConfig.getExtension();
|
||||
GeneratePreviewResp generatePreview = new GeneratePreviewResp();
|
||||
generatePreview.setBackend(isBackend);
|
||||
generatePreviewList.add(generatePreview);
|
||||
if (isBackend) {
|
||||
generatePreview.setFileName(className + extension);
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
|
||||
} else {
|
||||
generatePreview.setFileName(".vue".equals(extension) && "index".equals(templateConfigEntry.getKey())
|
||||
String fileName = className + extension;
|
||||
if (!isBackend) {
|
||||
fileName = ".vue".equals(extension) && "index".equals(classNameSuffix)
|
||||
? "index.vue"
|
||||
: this.getFrontendFileName(classNamePrefix, className, extension));
|
||||
genConfigMap.put("fieldConfigs", fieldConfigList);
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
|
||||
: this.getFrontendFileName(classNamePrefix, className, extension);
|
||||
}
|
||||
setPreviewPath(generatePreview, genConfig, templateConfig);
|
||||
generatePreview.setFileName(fileName);
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), BeanUtil
|
||||
.beanToMap(innerGenConfig)));
|
||||
this.setPreviewPath(generatePreview, innerGenConfig, templateConfig);
|
||||
}
|
||||
return generatePreviewList;
|
||||
}
|
||||
|
||||
private void setPreviewPath(GeneratePreviewResp generatePreview,
|
||||
GenConfigDO genConfig,
|
||||
InnerGenConfigDO genConfig,
|
||||
GeneratorProperties.TemplateConfig templateConfig) {
|
||||
// 获取前后端基础路径
|
||||
String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
|
||||
String frontendBasicPackagePath = String.join(File.separator, projectProperties.getAppName(), projectProperties
|
||||
.getAppName() + "-ui");
|
||||
String packageName = genConfig.getPackageName();
|
||||
String moduleName = StrUtil.subSuf(packageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
String packagePath;
|
||||
if (generatePreview.isBackend()) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
|
||||
@@ -271,7 +269,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
} else {
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system
|
||||
packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.SLASH, File.separator), moduleName);
|
||||
.replace(StringConstants.SLASH, File.separator), genConfig.getApiModuleName());
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system/user
|
||||
packagePath = ".vue".equals(templateConfig.getExtension())
|
||||
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
|
||||
@@ -281,7 +279,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<String> tableNames, HttpServletRequest request, HttpServletResponse response) {
|
||||
public void generate(List<String> tableNames, HttpServletResponse response) {
|
||||
try {
|
||||
String tempDir = SystemUtil.getUserInfo().getTempDir();
|
||||
// 删除旧代码
|
||||
@@ -296,7 +294,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
File tempDirFile = new File(tempDir, projectProperties.getAppName());
|
||||
String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT;
|
||||
ZipUtil.zip(tempDirFile.getPath(), zipFilePath);
|
||||
FileUploadUtils.download(request, response, new File(zipFilePath), true);
|
||||
FileUploadUtils.download(response, new File(zipFilePath));
|
||||
} catch (Exception e) {
|
||||
log.error("Generate code of table '{}' occurred an error. {}", tableNames, e.getMessage(), e);
|
||||
throw new BusinessException("代码生成失败,请手动清理生成文件");
|
||||
@@ -350,42 +348,36 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
/**
|
||||
* 预处理生成配置
|
||||
*
|
||||
* @param genConfigMap 生成配置
|
||||
* @param originFieldConfigList 原始字段配置列表
|
||||
* @param templateConfigEntry 模板配置
|
||||
* @param genConfig 生成配置
|
||||
*/
|
||||
private void pretreatment(Map<String, Object> genConfigMap,
|
||||
List<FieldConfigDO> originFieldConfigList,
|
||||
Map.Entry<String, GeneratorProperties.TemplateConfig> templateConfigEntry) {
|
||||
GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
|
||||
// 移除需要忽略的字段
|
||||
List<FieldConfigDO> fieldConfigList = originFieldConfigList.stream()
|
||||
.filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig.getExcludeFields()))
|
||||
.toList();
|
||||
genConfigMap.put("fieldConfigs", fieldConfigList);
|
||||
private void pretreatment(InnerGenConfigDO genConfig) {
|
||||
List<FieldConfigDO> fieldConfigList = genConfig.getFieldConfigs();
|
||||
// 统计部分特殊字段特征
|
||||
genConfigMap.put("hasLocalDateTime", false);
|
||||
genConfigMap.put("hasBigDecimal", false);
|
||||
genConfigMap.put("hasRequiredField", false);
|
||||
genConfigMap.put("hasListQueryField", false);
|
||||
Set<String> dictCodeSet = new HashSet<>();
|
||||
for (FieldConfigDO fieldConfig : fieldConfigList) {
|
||||
String fieldType = fieldConfig.getFieldType();
|
||||
if ("LocalDateTime".equals(fieldType)) {
|
||||
genConfigMap.put("hasLocalDateTime", true);
|
||||
}
|
||||
if ("BigDecimal".equals(fieldType)) {
|
||||
genConfigMap.put("hasBigDecimal", true);
|
||||
}
|
||||
// 必填项
|
||||
if (Boolean.TRUE.equals(fieldConfig.getIsRequired())) {
|
||||
genConfigMap.put("hasRequiredField", true);
|
||||
genConfig.setHasRequiredField(true);
|
||||
}
|
||||
// 数据类型
|
||||
if ("BigDecimal".equals(fieldType)) {
|
||||
genConfig.setHasBigDecimalField(true);
|
||||
}
|
||||
if (TIME_PACKAGE_CLASS.contains(fieldType)) {
|
||||
genConfig.setHasTimeField(true);
|
||||
}
|
||||
QueryTypeEnum queryType = fieldConfig.getQueryType();
|
||||
if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN
|
||||
.name(), QueryTypeEnum.BETWEEN.name())) {
|
||||
genConfigMap.put("hasListQueryField", true);
|
||||
genConfig.setHasListField(true);
|
||||
}
|
||||
// 字典码
|
||||
if (StrUtil.isNotBlank(fieldConfig.getDictCode())) {
|
||||
genConfig.setHasDictField(true);
|
||||
dictCodeSet.add(fieldConfig.getDictCode());
|
||||
}
|
||||
}
|
||||
String subPackageName = templateConfig.getPackageName();
|
||||
genConfigMap.put("subPackageName", subPackageName);
|
||||
genConfig.setDictCodes(dictCodeSet);
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import ${packageName}.service.${classNamePrefix}Service;
|
||||
* ${businessName}管理 API
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Tag(name = "${businessName}管理 API")
|
||||
@RestController
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasLocalDateTime>
|
||||
import java.time.LocalDateTime;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimal>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
@@ -21,7 +21,7 @@ import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
|
||||
* ${businessName}详情信息
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasLocalDateTime>
|
||||
import java.time.LocalDateTime;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimal>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
@@ -18,7 +18,7 @@ import top.continew.starter.extension.crud.model.entity.BaseDO;
|
||||
* ${businessName}实体
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@TableName("${tableName}")
|
||||
|
@@ -1,12 +1,12 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
import ${packageName}.model.entity.${classNamePrefix}DO;
|
||||
|
||||
/**
|
||||
* ${businessName} Mapper
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
public interface ${className} extends BaseMapper<${classNamePrefix}DO> {}
|
@@ -2,13 +2,13 @@ package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
<#if hasLocalDateTime>
|
||||
import java.time.LocalDateTime;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimal>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
<#if hasListQueryField>
|
||||
<#if hasListField>
|
||||
import java.util.List;
|
||||
</#if>
|
||||
|
||||
@@ -23,7 +23,7 @@ import top.continew.starter.data.core.enums.QueryType;
|
||||
* ${businessName}查询条件
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "${businessName}查询条件")
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasLocalDateTime>
|
||||
import java.time.LocalDateTime;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimal>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
@@ -24,7 +24,7 @@ import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
* 创建或修改${businessName}信息
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建或修改${businessName}信息")
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasLocalDateTime>
|
||||
import java.time.LocalDateTime;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimal>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
@@ -18,7 +18,7 @@ import top.continew.starter.extension.crud.model.resp.BaseResp;
|
||||
* ${businessName}信息
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "${businessName}信息")
|
||||
|
@@ -10,6 +10,6 @@ import ${packageName}.model.resp.${classNamePrefix}Resp;
|
||||
* ${businessName}业务接口
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
public interface ${className} extends BaseService<${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> {}
|
@@ -17,7 +17,7 @@ import ${packageName}.service.${classNamePrefix}Service;
|
||||
* ${businessName}业务实现
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
|
@@ -29,15 +29,9 @@ const isUpdate = computed(() => !!dataId.value)
|
||||
const title = computed(() => (isUpdate.value ? '修改${businessName}' : '新增${businessName}'))
|
||||
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInForm>
|
||||
<#-- SELECT/RADIO/CHECK_BOX/TREE_SELECT控件从服务器端获取数据 -->
|
||||
<#if fieldConfig.formType = 'SELECT' || fieldConfig.formType = 'RADIO'
|
||||
|| fieldConfig.formType = 'CHECK_BOX' || fieldConfig.formType = 'TREE_SELECT'>
|
||||
const { ${fieldConfig.columnName}_enum } = useDict('${fieldConfig.columnName}_enum')
|
||||
<#if hasDictField>
|
||||
const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const options: Options = {
|
||||
form: {},
|
||||
@@ -67,16 +61,15 @@ const columns: Columns = reactive([
|
||||
type: 'switch',
|
||||
<#elseif fieldConfig.formType = 'CHECK_BOX'>
|
||||
type: 'check-group',
|
||||
options: ${fieldConfig.columnName}_enum,
|
||||
<#elseif fieldConfig.formType = 'TREE_SELECT'>
|
||||
type: 'tree-select',
|
||||
data: '${fieldConfig.columnName}_enum',
|
||||
<#elseif fieldConfig.formType = 'SELECT'>
|
||||
type: 'select',
|
||||
options: ${fieldConfig.columnName}_enum,
|
||||
<#elseif fieldConfig.formType = 'RADIO'>
|
||||
type: 'radio-group',
|
||||
options: ${fieldConfig.columnName}_enum,
|
||||
type: 'radio-group'
|
||||
</#if>
|
||||
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != ''>
|
||||
options: ${fieldConfig.dictCode},
|
||||
</#if>
|
||||
<#if fieldConfig.isRequired>
|
||||
rules: [{ required: true, message: '请输入${fieldConfig.comment}' }]
|
||||
|
@@ -12,62 +12,51 @@
|
||||
:disabled-column-keys="['name']"
|
||||
@refresh="search"
|
||||
>
|
||||
<#-- 查询字段配置 -->
|
||||
<template #custom-left>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
<#if fieldConfig.formType == "SELECT"><#-- 下拉框 -->
|
||||
<a-select
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
:options="${fieldConfig.columnName}_enum"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
allow-clear
|
||||
style="width: 150px"
|
||||
@change="search"
|
||||
/>
|
||||
<#elseif fieldConfig.formType == "RADIO"><#-- 单选框 -->
|
||||
<a-radio-group v-model="queryForm.${fieldConfig.fieldName}" :options="${fieldConfig.columnName}_enum" @change="search"/>
|
||||
<#elseif fieldConfig.formType == "DATE"><#-- 日期框 -->
|
||||
<#if fieldConfig.queryType == "BETWEEN">
|
||||
<a-range-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
:placeholder="['请选择开始${fieldConfig.comment}','请选择结束${fieldConfig.comment}']"
|
||||
format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<#else>
|
||||
<a-date-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</#if>
|
||||
|
||||
<#elseif fieldConfig.formType == "DATE_TIME"><#-- 日期时间框 -->
|
||||
<#if fieldConfig.queryType == "BETWEEN">
|
||||
<a-range-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
:placeholder="['请选择开始${fieldConfig.comment}','请选择结束${fieldConfig.comment}']"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<#else>
|
||||
<a-date-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</#if>
|
||||
<#else>
|
||||
<a-input v-model="queryForm.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" allow-clear @change="search">
|
||||
<template #prefix><icon-search /></template>
|
||||
</a-input>
|
||||
</#if>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
<#if fieldConfig.formType == "SELECT"><#-- 下拉框 -->
|
||||
<a-select
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
:options="${fieldConfig.columnName}_enum"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
allow-clear
|
||||
style="width: 150px"
|
||||
@change="search"
|
||||
/>
|
||||
<#elseif fieldConfig.formType == "RADIO"><#-- 单选框 -->
|
||||
<a-radio-group v-model="queryForm.${fieldConfig.fieldName}" :options="${fieldConfig.dictCode}" @change="search"/>
|
||||
<#elseif fieldConfig.formType == "DATE"><#-- 日期框 -->
|
||||
<#if fieldConfig.queryType == "BETWEEN">
|
||||
<DateRangePicker v-model="queryForm.${fieldConfig.fieldName}" format="YYYY-MM-DD" @change="search" />
|
||||
<#else>
|
||||
<a-date-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
format="YYYY-MM-DD"
|
||||
style="height: 32px"
|
||||
/>
|
||||
</#if>
|
||||
</#list>
|
||||
<#elseif fieldConfig.formType == "DATE_TIME"><#-- 日期时间框 -->
|
||||
<#if fieldConfig.queryType == "BETWEEN">
|
||||
<DateRangePicker v-model="queryForm.${fieldConfig.fieldName}" @change="search" />
|
||||
<#else>
|
||||
<a-date-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="height: 32px"
|
||||
/>
|
||||
</#if>
|
||||
<#else>
|
||||
<a-input v-model="queryForm.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" allow-clear @change="search">
|
||||
<template #prefix><icon-search /></template>
|
||||
</a-input>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</template>
|
||||
<template #custom-right>
|
||||
@@ -83,9 +72,19 @@
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<#-- 列字段配置 -->
|
||||
<template #name="{ record }">
|
||||
<a-link @click="onDetail(record)">{{ record.name }}</a-link>
|
||||
</template>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInList>
|
||||
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != "">
|
||||
<template #${fieldConfig.fieldName}="{ record }">
|
||||
<GiCellTag :value="record.${fieldConfig.fieldName}" :dict="${fieldConfig.dictCode}" />
|
||||
</template>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-link v-permission="['${apiModuleName}:${apiName}:update']" @click="onUpdate(record)">修改</a-link>
|
||||
@@ -109,7 +108,7 @@
|
||||
<script setup lang="ts">
|
||||
import ${classNamePrefix}AddModal from './${classNamePrefix}AddModal.vue'
|
||||
import ${classNamePrefix}DetailDrawer from './${classNamePrefix}DetailDrawer.vue'
|
||||
import { type ${classNamePrefix}Resp, type ${classNamePrefix}Query, delete${classNamePrefix}, export${classNamePrefix}, list${classNamePrefix} } from '@/apis/${apiModuleName}/${apiName}'
|
||||
import { type ${classNamePrefix}Resp, type ${classNamePrefix}Query, delete${classNamePrefix}, export${classNamePrefix}, list${classNamePrefix} } from '@/apis/${apiModuleName}'
|
||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||
import { useDownload, useTable } from '@/hooks'
|
||||
import { isMobile } from '@/utils'
|
||||
@@ -118,6 +117,10 @@ import { useDict } from '@/hooks/app'
|
||||
|
||||
defineOptions({ name: '${classNamePrefix}' })
|
||||
|
||||
<#if hasDictField>
|
||||
const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
|
||||
</#if>
|
||||
|
||||
const queryForm = reactive<${classNamePrefix}Query>({
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
@@ -127,14 +130,6 @@ const queryForm = reactive<${classNamePrefix}Query>({
|
||||
sort: ['createTime,desc']
|
||||
})
|
||||
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
<#if fieldConfig.formType == "SELECT" || fieldConfig.formType == "RADIO">
|
||||
const { ${fieldConfig.columnName}_enum } = useDict('${fieldConfig.columnName}_enum')
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const {
|
||||
tableData: dataList,
|
||||
loading,
|
||||
|
@@ -51,4 +51,16 @@ public class OnlineUserQuery implements Serializable {
|
||||
@Schema(description = "登录时间", example = "2023-08-08 00:00:00,2023-08-08 23:59:59")
|
||||
@DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
|
||||
private List<Date> loginTime;
|
||||
|
||||
/**
|
||||
* 用户 ID
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 角色 ID
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private Long roleId;
|
||||
}
|
||||
|
@@ -59,16 +59,9 @@ public interface OnlineUserService {
|
||||
LocalDateTime getLastActiveTime(String token);
|
||||
|
||||
/**
|
||||
* 根据角色 ID 清除
|
||||
*
|
||||
* @param roleId 角色 ID
|
||||
*/
|
||||
void cleanByRoleId(Long roleId);
|
||||
|
||||
/**
|
||||
* 根据用户 ID 清除登录
|
||||
* 踢出用户
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
*/
|
||||
void cleanByUserId(Long userId);
|
||||
void kickOut(Long userId);
|
||||
}
|
||||
|
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.auth.service;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 权限业务接口
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/3/2 20:40
|
||||
*/
|
||||
public interface PermissionService {
|
||||
|
||||
/**
|
||||
* 根据用户 ID 查询权限码
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
* @return 权限码集合
|
||||
*/
|
||||
Set<String> listPermissionByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户 ID 查询角色编码
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
* @return 角色编码集合
|
||||
*/
|
||||
Set<String> listRoleCodeByUserId(Long userId);
|
||||
}
|
@@ -36,18 +36,17 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.auth.model.resp.RouteResp;
|
||||
import top.continew.admin.auth.service.LoginService;
|
||||
import top.continew.admin.auth.service.PermissionService;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.common.constant.RegexConstants;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.common.enums.GenderEnum;
|
||||
import top.continew.admin.system.enums.MenuTypeEnum;
|
||||
import top.continew.admin.system.enums.MessageTypeEnum;
|
||||
import top.continew.admin.common.model.dto.LoginUser;
|
||||
import top.continew.admin.common.model.dto.RoleDTO;
|
||||
import top.continew.admin.common.util.helper.LoginHelper;
|
||||
import top.continew.admin.system.enums.MenuTypeEnum;
|
||||
import top.continew.admin.system.enums.MessageTemplateEnum;
|
||||
import top.continew.admin.system.enums.MessageTypeEnum;
|
||||
import top.continew.admin.system.enums.PasswordPolicyEnum;
|
||||
import top.continew.admin.system.model.entity.DeptDO;
|
||||
import top.continew.admin.system.model.entity.RoleDO;
|
||||
@@ -81,17 +80,16 @@ import static top.continew.admin.system.enums.PasswordPolicyEnum.PASSWORD_EXPIRA
|
||||
public class LoginServiceImpl implements LoginService {
|
||||
|
||||
private final ProjectProperties projectProperties;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
private final UserService userService;
|
||||
private final DeptService deptService;
|
||||
private final RoleService roleService;
|
||||
private final MenuService menuService;
|
||||
private final PermissionService permissionService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final UserSocialService userSocialService;
|
||||
private final MessageService messageService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final OptionService optionService;
|
||||
private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
private final MessageService messageService;
|
||||
|
||||
@Override
|
||||
public String accountLogin(String username, String password, HttpServletRequest request) {
|
||||
@@ -163,7 +161,7 @@ public class LoginServiceImpl implements LoginService {
|
||||
|
||||
@Override
|
||||
public List<RouteResp> buildRouteTree(Long userId) {
|
||||
Set<String> roleCodeSet = permissionService.listRoleCodeByUserId(userId);
|
||||
Set<String> roleCodeSet = roleService.listCodeByUserId(userId);
|
||||
if (CollUtil.isEmpty(roleCodeSet)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
@@ -205,17 +203,15 @@ public class LoginServiceImpl implements LoginService {
|
||||
*/
|
||||
private String login(UserDO user) {
|
||||
Long userId = user.getId();
|
||||
CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> permissionService
|
||||
CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> roleService
|
||||
.listPermissionByUserId(userId), threadPoolTaskExecutor);
|
||||
CompletableFuture<Set<String>> roleCodeFuture = CompletableFuture.supplyAsync(() -> permissionService
|
||||
.listRoleCodeByUserId(userId), threadPoolTaskExecutor);
|
||||
CompletableFuture<Set<RoleDTO>> roleFuture = CompletableFuture.supplyAsync(() -> roleService
|
||||
.listByUserId(userId), threadPoolTaskExecutor);
|
||||
CompletableFuture<Integer> passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService
|
||||
.getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name()));
|
||||
CompletableFuture.allOf(permissionFuture, roleCodeFuture, roleFuture);
|
||||
LoginUser loginUser = new LoginUser(permissionFuture.join(), roleCodeFuture.join(), roleFuture
|
||||
.join(), passwordExpirationDaysFuture.join());
|
||||
CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture);
|
||||
LoginUser loginUser = new LoginUser(permissionFuture.join(), roleFuture.join(), passwordExpirationDaysFuture
|
||||
.join());
|
||||
BeanUtil.copyProperties(user, loginUser);
|
||||
return LoginHelper.login(loginUser);
|
||||
}
|
||||
|
@@ -18,12 +18,12 @@ package top.continew.admin.auth.service.impl;
|
||||
|
||||
import cn.crane4j.annotation.AutoOperate;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.continew.admin.auth.model.query.OnlineUserQuery;
|
||||
import top.continew.admin.auth.model.resp.OnlineUserResp;
|
||||
@@ -44,10 +44,10 @@ import java.util.List;
|
||||
* 在线用户业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @author Lion Li(<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>)
|
||||
* @since 2023/3/25 22:49
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OnlineUserServiceImpl implements OnlineUserService {
|
||||
|
||||
@Override
|
||||
@@ -63,18 +63,18 @@ public class OnlineUserServiceImpl implements OnlineUserService {
|
||||
List<LoginUser> loginUserList = new ArrayList<>();
|
||||
// 查询所有登录用户
|
||||
List<String> tokenKeyList = StpUtil.searchTokenValue(StringConstants.EMPTY, 0, -1, false);
|
||||
for (String tokenKey : tokenKeyList) {
|
||||
tokenKeyList.parallelStream().forEach(tokenKey -> {
|
||||
String token = StrUtil.subAfter(tokenKey, StringConstants.COLON, true);
|
||||
// 忽略已过期或失效 Token
|
||||
if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < SaTokenDao.NEVER_EXPIRE) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
// 检查是否符合查询条件
|
||||
LoginUser loginUser = LoginHelper.getLoginUser(token);
|
||||
if (this.isMatchQuery(query, loginUser)) {
|
||||
loginUserList.add(loginUser);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 设置排序
|
||||
CollUtil.sort(loginUserList, Comparator.comparing(LoginUser::getLoginTime).reversed());
|
||||
return loginUserList;
|
||||
@@ -87,20 +87,7 @@ public class OnlineUserServiceImpl implements OnlineUserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanByRoleId(Long roleId) {
|
||||
List<LoginUser> loginUserList = this.list(new OnlineUserQuery());
|
||||
loginUserList.parallelStream().forEach(u -> {
|
||||
if (u.getRoles().stream().anyMatch(r -> r.getId().equals(roleId))) {
|
||||
try {
|
||||
StpUtil.logoutByTokenValue(u.getToken());
|
||||
} catch (NotLoginException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanByUserId(Long userId) {
|
||||
public void kickOut(Long userId) {
|
||||
if (!StpUtil.isLogin(userId)) {
|
||||
return;
|
||||
}
|
||||
@@ -121,13 +108,22 @@ public class OnlineUserServiceImpl implements OnlineUserService {
|
||||
flag1 = StrUtil.contains(loginUser.getUsername(), nickname) || StrUtil.contains(LoginHelper
|
||||
.getNickname(loginUser.getId()), nickname);
|
||||
}
|
||||
|
||||
boolean flag2 = true;
|
||||
List<Date> loginTime = query.getLoginTime();
|
||||
if (CollUtil.isNotEmpty(loginTime)) {
|
||||
flag2 = DateUtil.isIn(DateUtil.date(loginUser.getLoginTime()).toJdkDate(), loginTime.get(0), loginTime
|
||||
.get(1));
|
||||
}
|
||||
return flag1 && flag2;
|
||||
boolean flag3 = true;
|
||||
Long userId = query.getUserId();
|
||||
if (null != userId) {
|
||||
flag3 = userId.equals(loginUser.getId());
|
||||
}
|
||||
boolean flag4 = true;
|
||||
Long roleId = query.getRoleId();
|
||||
if (null != roleId) {
|
||||
flag4 = loginUser.getRoles().stream().anyMatch(r -> r.getId().equals(roleId));
|
||||
}
|
||||
return flag1 && flag2 && flag3 && flag4;
|
||||
}
|
||||
}
|
||||
|
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.auth.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.continew.admin.auth.service.PermissionService;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.system.service.MenuService;
|
||||
import top.continew.admin.system.service.RoleService;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 权限业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/3/2 20:40
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionServiceImpl implements PermissionService {
|
||||
|
||||
private final MenuService menuService;
|
||||
private final RoleService roleService;
|
||||
|
||||
@Override
|
||||
public Set<String> listPermissionByUserId(Long userId) {
|
||||
Set<String> roleCodeSet = this.listRoleCodeByUserId(userId);
|
||||
// 超级管理员赋予全部权限
|
||||
if (roleCodeSet.contains(SysConstants.ADMIN_ROLE_CODE)) {
|
||||
return CollUtil.newHashSet(SysConstants.ALL_PERMISSION);
|
||||
}
|
||||
return menuService.listPermissionByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> listRoleCodeByUserId(Long userId) {
|
||||
return roleService.listCodeByUserId(userId);
|
||||
}
|
||||
}
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import top.continew.admin.system.model.entity.DeptDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 部门 Mapper
|
||||
|
@@ -18,7 +18,7 @@ package top.continew.admin.system.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.admin.system.model.entity.DictItemDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
|
||||
import java.util.List;
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import top.continew.admin.system.model.entity.DictDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 字典 Mapper
|
||||
|
@@ -19,7 +19,7 @@ package top.continew.admin.system.mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.system.model.entity.FileDO;
|
||||
import top.continew.admin.system.model.resp.FileStatisticsResp;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -25,7 +25,7 @@ import top.continew.admin.system.model.resp.DashboardAccessTrendResp;
|
||||
import top.continew.admin.system.model.resp.DashboardPopularModuleResp;
|
||||
import top.continew.admin.system.model.resp.DashboardTotalResp;
|
||||
import top.continew.admin.system.model.resp.log.LogResp;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@@ -18,7 +18,7 @@ package top.continew.admin.system.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.admin.system.model.entity.MenuDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@@ -22,7 +22,7 @@ import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.admin.system.model.entity.MessageDO;
|
||||
import top.continew.admin.system.model.resp.MessageResp;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 消息 Mapper
|
||||
|
@@ -18,7 +18,7 @@ package top.continew.admin.system.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.admin.system.model.entity.MessageUserDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 消息和用户 Mapper
|
||||
|
@@ -18,7 +18,7 @@ package top.continew.admin.system.mapper;
|
||||
|
||||
import top.continew.admin.system.model.entity.NoticeDO;
|
||||
import top.continew.admin.system.model.resp.DashboardNoticeResp;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -19,7 +19,7 @@ package top.continew.admin.system.mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.system.model.entity.OptionDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -19,7 +19,7 @@ package top.continew.admin.system.mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.system.model.entity.RoleDeptDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import top.continew.admin.system.model.entity.RoleDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 角色 Mapper
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import top.continew.admin.system.model.entity.RoleMenuDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import top.continew.admin.system.model.entity.StorageDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 存储 Mapper
|
||||
|
@@ -24,7 +24,7 @@ import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.common.config.mybatis.DataPermissionMapper;
|
||||
import top.continew.admin.system.model.entity.UserDO;
|
||||
import top.continew.admin.system.model.resp.UserDetailResp;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataPermission;
|
||||
import top.continew.starter.data.mp.datapermission.DataPermission;
|
||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
|
||||
import java.util.List;
|
||||
@@ -44,7 +44,7 @@ public interface UserMapper extends DataPermissionMapper<UserDO> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 分页列表信息
|
||||
*/
|
||||
@DataPermission("t1")
|
||||
@DataPermission(tableAlias = "t1")
|
||||
IPage<UserDetailResp> selectUserPage(@Param("page") IPage<UserDO> page,
|
||||
@Param(Constants.WRAPPER) QueryWrapper<UserDO> queryWrapper);
|
||||
|
||||
@@ -54,7 +54,7 @@ public interface UserMapper extends DataPermissionMapper<UserDO> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 列表信息
|
||||
*/
|
||||
@DataPermission("t1")
|
||||
@DataPermission(tableAlias = "t1")
|
||||
List<UserDetailResp> selectUserList(@Param(Constants.WRAPPER) QueryWrapper<UserDO> queryWrapper);
|
||||
|
||||
/**
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package top.continew.admin.system.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
import top.continew.admin.system.model.entity.UserPasswordHistoryDO;
|
||||
|
||||
/**
|
||||
|
@@ -19,7 +19,7 @@ package top.continew.admin.system.mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.system.model.entity.UserRoleDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -18,7 +18,7 @@ package top.continew.admin.system.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.admin.system.model.entity.UserSocialDO;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 用户社会化关联 Mapper
|
||||
|
@@ -18,6 +18,7 @@ package top.continew.admin.system.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import top.continew.starter.extension.crud.annotation.DictField;
|
||||
import top.continew.starter.extension.crud.model.entity.BaseDO;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -29,6 +30,7 @@ import java.io.Serial;
|
||||
* @since 2023/9/11 21:29
|
||||
*/
|
||||
@Data
|
||||
@DictField(valueKey = "code")
|
||||
@TableName("sys_dict")
|
||||
public class DictDO extends BaseDO {
|
||||
|
||||
|
@@ -67,4 +67,14 @@ public class RoleDO extends BaseDO {
|
||||
* 是否为系统内置数据
|
||||
*/
|
||||
private Boolean isSystem;
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 部门选择是否父子节点关联
|
||||
*/
|
||||
private Boolean deptCheckStrictly;
|
||||
}
|
||||
|
@@ -42,5 +42,5 @@ public class RoleQuery implements Serializable {
|
||||
*/
|
||||
@Schema(description = "关键词", example = "测试人员")
|
||||
@Query(columns = {"name", "code", "description"}, type = QueryType.LIKE)
|
||||
private String name;
|
||||
private String description;
|
||||
}
|
||||
|
@@ -90,4 +90,16 @@ public class RoleReq extends BaseReq {
|
||||
*/
|
||||
@Schema(description = "权限范围:部门 ID 列表", example = "5")
|
||||
private List<Long> deptIds = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "菜单选择是否父子节点关联", example = "false")
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 部门选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "部门选择是否父子节点关联", example = "false")
|
||||
private Boolean deptCheckStrictly;
|
||||
}
|
||||
|
@@ -81,6 +81,18 @@ public class RoleDetailResp extends BaseDetailResp {
|
||||
@ExcelProperty(value = "系统内置")
|
||||
private Boolean isSystem;
|
||||
|
||||
/**
|
||||
* 菜单选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "菜单选择是否父子节点关联", example = "false")
|
||||
private Boolean menuCheckStrictly;
|
||||
|
||||
/**
|
||||
* 部门选择是否父子节点关联
|
||||
*/
|
||||
@Schema(description = "部门选择是否父子节点关联", example = "false")
|
||||
private Boolean deptCheckStrictly;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
|
@@ -20,7 +20,7 @@ import top.continew.admin.system.model.entity.DeptDO;
|
||||
import top.continew.admin.system.model.query.DeptQuery;
|
||||
import top.continew.admin.system.model.req.DeptReq;
|
||||
import top.continew.admin.system.model.resp.DeptResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
import java.util.List;
|
||||
|
@@ -20,7 +20,7 @@ import top.continew.admin.system.model.entity.DictItemDO;
|
||||
import top.continew.admin.system.model.query.DictItemQuery;
|
||||
import top.continew.admin.system.model.req.DictItemReq;
|
||||
import top.continew.admin.system.model.resp.DictItemResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
@@ -48,4 +48,11 @@ public interface DictItemService extends BaseService<DictItemResp, DictItemResp,
|
||||
* @param dictIds 字典 ID 列表
|
||||
*/
|
||||
void deleteByDictIds(List<Long> dictIds);
|
||||
|
||||
/**
|
||||
* 查询枚举字典名称列表
|
||||
*
|
||||
* @return 枚举字典名称列表
|
||||
*/
|
||||
List<String> listEnumDictNames();
|
||||
}
|
@@ -20,9 +20,12 @@ import top.continew.admin.system.model.entity.DictDO;
|
||||
import top.continew.admin.system.model.query.DictQuery;
|
||||
import top.continew.admin.system.model.req.DictReq;
|
||||
import top.continew.admin.system.model.resp.DictResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典业务接口
|
||||
*
|
||||
@@ -30,4 +33,11 @@ import top.continew.starter.extension.crud.service.BaseService;
|
||||
* @since 2023/9/11 21:29
|
||||
*/
|
||||
public interface DictService extends BaseService<DictResp, DictResp, DictQuery, DictReq>, IService<DictDO> {
|
||||
|
||||
/**
|
||||
* 查询枚举字典
|
||||
*
|
||||
* @return 枚举字典列表
|
||||
*/
|
||||
List<LabelValueResp> listEnumDict();
|
||||
}
|
@@ -23,7 +23,7 @@ import top.continew.admin.system.model.query.FileQuery;
|
||||
import top.continew.admin.system.model.req.FileReq;
|
||||
import top.continew.admin.system.model.resp.FileResp;
|
||||
import top.continew.admin.system.model.resp.FileStatisticsResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
import java.util.List;
|
||||
|
@@ -21,7 +21,7 @@ import top.continew.admin.system.model.query.MenuQuery;
|
||||
import top.continew.admin.system.model.req.MenuReq;
|
||||
import top.continew.admin.system.model.resp.MenuResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@@ -23,7 +23,7 @@ import top.continew.admin.system.model.resp.NoticeDetailResp;
|
||||
import top.continew.admin.system.model.resp.NoticeResp;
|
||||
import top.continew.admin.system.model.resp.DashboardNoticeResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -22,7 +22,7 @@ import top.continew.admin.system.model.query.RoleQuery;
|
||||
import top.continew.admin.system.model.req.RoleReq;
|
||||
import top.continew.admin.system.model.resp.RoleDetailResp;
|
||||
import top.continew.admin.system.model.resp.RoleResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
import java.util.List;
|
||||
@@ -36,6 +36,14 @@ import java.util.Set;
|
||||
*/
|
||||
public interface RoleService extends BaseService<RoleResp, RoleDetailResp, RoleQuery, RoleReq>, IService<RoleDO> {
|
||||
|
||||
/**
|
||||
* 根据用户 ID 查询权限码
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
* @return 权限码集合
|
||||
*/
|
||||
Set<String> listPermissionByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据 ID 列表查询
|
||||
*
|
||||
|
@@ -20,7 +20,7 @@ import top.continew.admin.system.model.entity.StorageDO;
|
||||
import top.continew.admin.system.model.query.StorageQuery;
|
||||
import top.continew.admin.system.model.req.StorageReq;
|
||||
import top.continew.admin.system.model.resp.StorageResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
/**
|
||||
|
@@ -25,7 +25,7 @@ import top.continew.admin.system.model.resp.UserDetailResp;
|
||||
import top.continew.admin.system.model.resp.UserImportParseResp;
|
||||
import top.continew.admin.system.model.resp.UserImportResp;
|
||||
import top.continew.admin.system.model.resp.UserResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.data.mp.service.IService;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@@ -32,8 +32,8 @@ import top.continew.admin.system.service.DictItemService;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
|
||||
@@ -81,6 +81,11 @@ public class DictItemServiceImpl extends BaseServiceImpl<DictItemMapper, DictIte
|
||||
RedisUtils.deleteByPattern(CacheConstants.DICT_KEY_PREFIX + StringConstants.ASTERISK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> listEnumDictNames() {
|
||||
return ENUM_DICT_CACHE.keySet().stream().toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典值是否存在
|
||||
*
|
||||
|
@@ -26,6 +26,7 @@ import top.continew.admin.system.model.resp.DictResp;
|
||||
import top.continew.admin.system.service.DictItemService;
|
||||
import top.continew.admin.system.service.DictService;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
|
||||
import java.util.List;
|
||||
@@ -71,6 +72,12 @@ public class DictServiceImpl extends BaseServiceImpl<DictMapper, DictDO, DictRes
|
||||
dictItemService.deleteByDictIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LabelValueResp> listEnumDict() {
|
||||
List<String> enumDictNameList = dictItemService.listEnumDictNames();
|
||||
return enumDictNameList.stream().map(name -> new LabelValueResp(name, name)).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 名称是否存在
|
||||
*
|
||||
|
@@ -19,11 +19,11 @@ package top.continew.admin.system.service.impl;
|
||||
import cn.crane4j.annotation.AutoOperate;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -67,7 +67,8 @@ public class LogServiceImpl implements LogService {
|
||||
@Override
|
||||
public PageResp<LogResp> page(LogQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper<LogDO> queryWrapper = this.buildQueryWrapper(query);
|
||||
IPage<LogResp> page = baseMapper.selectLogPage(pageQuery.toPage(), queryWrapper);
|
||||
IPage<LogResp> page = baseMapper.selectLogPage(new Page<>(pageQuery.getPage(), pageQuery
|
||||
.getSize()), queryWrapper);
|
||||
return PageResp.build(page);
|
||||
}
|
||||
|
||||
@@ -132,12 +133,12 @@ public class LogServiceImpl implements LogService {
|
||||
* @param sortQuery 排序查询条件
|
||||
*/
|
||||
private void sort(QueryWrapper<LogDO> queryWrapper, SortQuery sortQuery) {
|
||||
Sort sort = Opt.ofNullable(sortQuery).orElseGet(SortQuery::new).getSort();
|
||||
for (Sort.Order order : sort) {
|
||||
if (null != order) {
|
||||
String property = order.getProperty();
|
||||
queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property));
|
||||
}
|
||||
if (sortQuery == null || sortQuery.getSort().isUnsorted()) {
|
||||
return;
|
||||
}
|
||||
for (Sort.Order order : sortQuery.getSort()) {
|
||||
String property = order.getProperty();
|
||||
queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -52,7 +52,12 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
|
||||
@Override
|
||||
public Long add(MenuReq req) {
|
||||
String title = req.getTitle();
|
||||
CheckUtils.throwIf(this.isNameExists(title, req.getParentId(), null), "新增失败,[{}] 已存在", title);
|
||||
CheckUtils.throwIf(this.isTitleExists(title, req.getParentId(), null), "新增失败,标题 [{}] 已存在", title);
|
||||
// 目录和菜单的组件名称不能重复
|
||||
if (!MenuTypeEnum.BUTTON.equals(req.getType())) {
|
||||
String name = req.getName();
|
||||
CheckUtils.throwIf(this.isNameExists(name, null), "新增失败,组件名称 [{}] 已存在", name);
|
||||
}
|
||||
// 目录类型菜单,默认为 Layout
|
||||
if (MenuTypeEnum.DIR.equals(req.getType())) {
|
||||
req.setComponent(StrUtil.blankToDefault(req.getComponent(), "Layout"));
|
||||
@@ -64,7 +69,12 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
|
||||
@Override
|
||||
public void update(MenuReq req, Long id) {
|
||||
String title = req.getTitle();
|
||||
CheckUtils.throwIf(this.isNameExists(title, req.getParentId(), id), "修改失败,[{}] 已存在", title);
|
||||
CheckUtils.throwIf(this.isTitleExists(title, req.getParentId(), id), "修改失败,标题 [{}] 已存在", title);
|
||||
// 目录和菜单的组件名称不能重复
|
||||
if (!MenuTypeEnum.BUTTON.equals(req.getType())) {
|
||||
String name = req.getName();
|
||||
CheckUtils.throwIf(this.isNameExists(name, id), "修改失败,组件名称 [{}] 已存在", name);
|
||||
}
|
||||
MenuDO oldMenu = super.getById(id);
|
||||
CheckUtils.throwIfNotEqual(req.getType(), oldMenu.getType(), "不允许修改菜单类型");
|
||||
super.update(req, id);
|
||||
@@ -100,18 +110,33 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
|
||||
}
|
||||
|
||||
/**
|
||||
* 名称是否存在
|
||||
* 标题是否存在
|
||||
*
|
||||
* @param name 名称
|
||||
* @param title 标题
|
||||
* @param parentId 上级 ID
|
||||
* @param id ID
|
||||
* @return 是否存在
|
||||
* @return true:存在;false:不存在
|
||||
*/
|
||||
private boolean isNameExists(String name, Long parentId, Long id) {
|
||||
private boolean isTitleExists(String title, Long parentId, Long id) {
|
||||
return baseMapper.lambdaQuery()
|
||||
.eq(MenuDO::getTitle, name)
|
||||
.eq(MenuDO::getTitle, title)
|
||||
.eq(MenuDO::getParentId, parentId)
|
||||
.ne(null != id, MenuDO::getId, id)
|
||||
.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 名称是否存在
|
||||
*
|
||||
* @param name 标题
|
||||
* @param id ID
|
||||
* @return true:存在;false:不存在
|
||||
*/
|
||||
private boolean isNameExists(String name, Long id) {
|
||||
return baseMapper.lambdaQuery()
|
||||
.eq(MenuDO::getName, name)
|
||||
.ne(MenuDO::getType, MenuTypeEnum.BUTTON)
|
||||
.ne(null != id, MenuDO::getId, id)
|
||||
.exists();
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -32,7 +33,7 @@ import top.continew.admin.system.model.resp.MessageResp;
|
||||
import top.continew.admin.system.service.MessageService;
|
||||
import top.continew.admin.system.service.MessageUserService;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.data.mybatis.plus.query.QueryWrapperHelper;
|
||||
import top.continew.starter.data.mp.util.QueryWrapperHelper;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
|
||||
@@ -54,10 +55,11 @@ public class MessageServiceImpl implements MessageService {
|
||||
@Override
|
||||
@AutoOperate(type = MessageResp.class, on = "list")
|
||||
public PageResp<MessageResp> page(MessageQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper<MessageDO> queryWrapper = QueryWrapperHelper.build(query);
|
||||
QueryWrapper<MessageDO> queryWrapper = QueryWrapperHelper.build(query, pageQuery.getSort());
|
||||
queryWrapper.apply(null != query.getUserId(), "t2.user_id={0}", query.getUserId())
|
||||
.apply(null != query.getIsRead(), "t2.is_read={0}", query.getIsRead());
|
||||
IPage<MessageResp> page = baseMapper.selectPageByUserId(pageQuery.toPage(), queryWrapper);
|
||||
IPage<MessageResp> page = baseMapper.selectPageByUserId(new Page<>(pageQuery.getPage(), pageQuery
|
||||
.getSize()), queryWrapper);
|
||||
return PageResp.build(page);
|
||||
}
|
||||
|
||||
|
@@ -38,7 +38,7 @@ import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.data.mybatis.plus.query.QueryWrapperHelper;
|
||||
import top.continew.starter.data.mp.util.QueryWrapperHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@@ -26,12 +26,15 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.auth.model.query.OnlineUserQuery;
|
||||
import top.continew.admin.auth.service.OnlineUserService;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.common.constant.ContainerConstants;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.enums.DataScopeEnum;
|
||||
import top.continew.admin.common.model.dto.LoginUser;
|
||||
import top.continew.admin.common.model.dto.RoleDTO;
|
||||
import top.continew.admin.common.util.helper.LoginHelper;
|
||||
import top.continew.admin.system.mapper.RoleMapper;
|
||||
import top.continew.admin.system.model.entity.RoleDO;
|
||||
import top.continew.admin.system.model.query.RoleQuery;
|
||||
@@ -57,10 +60,10 @@ import java.util.stream.Collectors;
|
||||
public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleResp, RoleDetailResp, RoleQuery, RoleReq> implements RoleService {
|
||||
|
||||
private final MenuService menuService;
|
||||
private final OnlineUserService onlineUserService;
|
||||
private final RoleMenuService roleMenuService;
|
||||
private final RoleDeptService roleDeptService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final OnlineUserService onlineUserService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -92,16 +95,23 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
|
||||
}
|
||||
// 更新信息
|
||||
super.update(req, id);
|
||||
// 更新关联信息
|
||||
if (!SysConstants.ADMIN_ROLE_CODE.equals(oldRole.getCode())) {
|
||||
// 保存角色和菜单关联
|
||||
boolean isSaveMenuSuccess = roleMenuService.add(req.getMenuIds(), id);
|
||||
// 保存角色和部门关联
|
||||
boolean isSaveDeptSuccess = roleDeptService.add(req.getDeptIds(), id);
|
||||
// 如果功能权限或数据权限有变更,则清除关联的在线用户(重新登录以获取最新角色权限)
|
||||
if (ObjectUtil.notEqual(req.getDataScope(), oldDataScope) || isSaveMenuSuccess || isSaveDeptSuccess) {
|
||||
onlineUserService.cleanByRoleId(id);
|
||||
}
|
||||
if (SysConstants.ADMIN_ROLE_CODE.equals(req.getCode())) {
|
||||
return;
|
||||
}
|
||||
// 保存角色和菜单关联
|
||||
boolean isSaveMenuSuccess = roleMenuService.add(req.getMenuIds(), id);
|
||||
// 保存角色和部门关联
|
||||
boolean isSaveDeptSuccess = roleDeptService.add(req.getDeptIds(), id);
|
||||
// 如果功能权限或数据权限有变更,则更新在线用户权限信息
|
||||
if (ObjectUtil.notEqual(req.getDataScope(), oldDataScope) || isSaveMenuSuccess || isSaveDeptSuccess) {
|
||||
OnlineUserQuery query = new OnlineUserQuery();
|
||||
query.setRoleId(id);
|
||||
List<LoginUser> loginUserList = onlineUserService.list(query);
|
||||
loginUserList.parallelStream().forEach(loginUser -> {
|
||||
loginUser.setRoles(this.listByUserId(loginUser.getId()));
|
||||
loginUser.setPermissions(this.listPermissionByUserId(loginUser.getId()));
|
||||
LoginHelper.updateLoginUser(loginUser, loginUser.getToken());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +146,16 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> listPermissionByUserId(Long userId) {
|
||||
Set<String> roleCodeSet = this.listCodeByUserId(userId);
|
||||
// 超级管理员赋予全部权限
|
||||
if (roleCodeSet.contains(SysConstants.ADMIN_ROLE_CODE)) {
|
||||
return CollUtil.newHashSet(SysConstants.ALL_PERMISSION);
|
||||
}
|
||||
return menuService.listPermissionByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ContainerMethod(namespace = ContainerConstants.USER_ROLE_NAME_LIST, type = MappingType.ORDER_OF_KEYS)
|
||||
public List<String> listNameByIds(List<Long> ids) {
|
||||
|
@@ -20,12 +20,14 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.validation.ValidationUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@@ -38,6 +40,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -49,11 +52,13 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import top.continew.admin.auth.model.query.OnlineUserQuery;
|
||||
import top.continew.admin.auth.service.OnlineUserService;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.common.constant.SysConstants;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.common.enums.GenderEnum;
|
||||
import top.continew.admin.common.model.dto.LoginUser;
|
||||
import top.continew.admin.common.util.SecureUtils;
|
||||
import top.continew.admin.common.util.helper.LoginHelper;
|
||||
import top.continew.admin.system.mapper.UserMapper;
|
||||
@@ -77,6 +82,7 @@ import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.service.CommonUserService;
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
import top.continew.starter.web.util.FileUploadUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
@@ -98,11 +104,11 @@ import static top.continew.admin.system.enums.PasswordPolicyEnum.*;
|
||||
@RequiredArgsConstructor
|
||||
public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserResp, UserDetailResp, UserQuery, UserReq> implements UserService, CommonUserService {
|
||||
|
||||
private final OnlineUserService onlineUserService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final OptionService optionService;
|
||||
private final UserPasswordHistoryService userPasswordHistoryService;
|
||||
private final OnlineUserService onlineUserService;
|
||||
private final OptionService optionService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final RoleService roleService;
|
||||
|
||||
@Resource
|
||||
@@ -113,7 +119,9 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
@Override
|
||||
public PageResp<UserResp> page(UserQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper<UserDO> queryWrapper = this.buildQueryWrapper(query);
|
||||
IPage<UserDetailResp> page = baseMapper.selectUserPage(pageQuery.toPage(), queryWrapper);
|
||||
super.sort(queryWrapper, pageQuery);
|
||||
IPage<UserDetailResp> page = baseMapper.selectUserPage(new Page<>(pageQuery.getPage(), pageQuery
|
||||
.getSize()), queryWrapper);
|
||||
PageResp<UserResp> pageResp = PageResp.build(page, super.getListClass());
|
||||
pageResp.getList().forEach(this::fill);
|
||||
return pageResp;
|
||||
@@ -129,13 +137,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
@Override
|
||||
public void downloadImportUserTemplate(HttpServletResponse response) throws IOException {
|
||||
try {
|
||||
byte[] bytes = ResourceUtil.readBytes("templates/import/userImportTemplate.xlsx");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("用户导入模板.xlsx"));
|
||||
response.addHeader("Content-Length", String.valueOf(bytes.length));
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
IoUtil.write(response.getOutputStream(), true, bytes);
|
||||
FileUploadUtils.download(response, ResourceUtil
|
||||
.getStream("templates/import/userImportTemplate.xlsx"), "用户导入模板.xlsx");
|
||||
} catch (Exception e) {
|
||||
log.error("下载用户导入模板失败:", e);
|
||||
response.setCharacterEncoding(CharsetUtil.UTF_8);
|
||||
@@ -323,9 +326,21 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
baseMapper.updateById(newUser);
|
||||
// 保存用户和角色关联
|
||||
boolean isSaveUserRoleSuccess = userRoleService.add(req.getRoleIds(), id);
|
||||
// 如果功能权限或数据权限有变更,则清除关联的在线用户(重新登录以获取最新角色权限)
|
||||
if (DisEnableStatusEnum.DISABLE.equals(newStatus) || isSaveUserRoleSuccess) {
|
||||
onlineUserService.cleanByUserId(id);
|
||||
// 如果禁用用户,则踢出在线用户
|
||||
if (DisEnableStatusEnum.DISABLE.equals(newStatus)) {
|
||||
onlineUserService.kickOut(id);
|
||||
return;
|
||||
}
|
||||
// 如果角色有变更,则更新在线用户权限信息
|
||||
if (isSaveUserRoleSuccess) {
|
||||
OnlineUserQuery query = new OnlineUserQuery();
|
||||
query.setUserId(id);
|
||||
List<LoginUser> loginUserList = onlineUserService.list(query);
|
||||
loginUserList.parallelStream().forEach(loginUser -> {
|
||||
loginUser.setRoles(roleService.listByUserId(loginUser.getId()));
|
||||
loginUser.setPermissions(roleService.listPermissionByUserId(loginUser.getId()));
|
||||
LoginHelper.updateLoginUser(loginUser, loginUser.getToken());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,10 +366,12 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
|
||||
@Override
|
||||
public void resetPassword(UserPasswordResetReq req, Long id) {
|
||||
UserDO user = super.getById(id);
|
||||
user.setPassword(req.getNewPassword());
|
||||
user.setPwdResetTime(LocalDateTime.now());
|
||||
baseMapper.updateById(user);
|
||||
super.getById(id);
|
||||
baseMapper.lambdaUpdate()
|
||||
.set(UserDO::getPassword, req.getNewPassword())
|
||||
.set(UserDO::getPwdResetTime, LocalDateTime.now())
|
||||
.eq(UserDO::getId, id)
|
||||
.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -399,9 +416,11 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
// 校验密码合法性
|
||||
int passwordRepetitionTimes = this.checkPassword(newPassword, user);
|
||||
// 更新密码和密码重置时间
|
||||
user.setPassword(newPassword);
|
||||
user.setPwdResetTime(LocalDateTime.now());
|
||||
baseMapper.updateById(user);
|
||||
baseMapper.lambdaUpdate()
|
||||
.set(UserDO::getPassword, newPassword)
|
||||
.set(UserDO::getPwdResetTime, LocalDateTime.now())
|
||||
.eq(UserDO::getId, id)
|
||||
.update();
|
||||
// 保存历史密码
|
||||
userPasswordHistoryService.add(id, password, passwordRepetitionTimes);
|
||||
// 修改后登出
|
||||
|
@@ -17,18 +17,18 @@
|
||||
<!-- 启动类 -->
|
||||
<main-class>top.continew.admin.ContiNewAdminApplication</main-class>
|
||||
<!-- 程序 jar 输出目录 -->
|
||||
<bin-path>bin</bin-path>
|
||||
<bin-path>bin/</bin-path>
|
||||
<!-- 配置文件输出目录 -->
|
||||
<config-path>config</config-path>
|
||||
<config-path>config/</config-path>
|
||||
<!-- 依赖 jar 输出目录 -->
|
||||
<lib-path>lib</lib-path>
|
||||
<lib-path>lib/</lib-path>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- ContiNew Starter 日志模块 - HttpTracePro(Spring Boot Actuator HttpTrace 定制增强版) -->
|
||||
<!-- ContiNew Starter 日志模块 - 拦截器版(Spring Boot Actuator HttpTrace 增强版) -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-httptrace-pro</artifactId>
|
||||
<artifactId>continew-starter-log-interceptor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 系统管理模块(存放系统管理模块相关功能,例如:部门管理、角色管理、用户管理等) -->
|
||||
@@ -75,19 +75,22 @@
|
||||
<!-- 排除配置文件 -->
|
||||
<excludes>
|
||||
<exclude>${config-path}/</exclude>
|
||||
<exclude>db/</exclude>
|
||||
<exclude>templates/</exclude>
|
||||
<exclude>logback-spring.xml</exclude>
|
||||
</excludes>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>${main-class}</mainClass>
|
||||
<!-- 为 MANIFEST.MF 中的 Class-Path 加入依赖 jar 目录前缀 -->
|
||||
<classpathPrefix>../${lib-path}/</classpathPrefix>
|
||||
<classpathPrefix>../${lib-path}</classpathPrefix>
|
||||
<addClasspath>true</addClasspath>
|
||||
<!-- jar 包不包含唯一版本标识 -->
|
||||
<useUniqueVersions>false</useUniqueVersions>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<!--为 MANIFEST.MF 中的 Class-Path 加入配置文件目录前缀 -->
|
||||
<Class-Path>../${config-path}/</Class-Path>
|
||||
<Class-Path>../${config-path}</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<outputDirectory>${project.build.directory}/app/${bin-path}</outputDirectory>
|
||||
@@ -123,14 +126,19 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/${config-path}</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>${config-path}/</include>
|
||||
<include>db/</include>
|
||||
<include>templates/</include>
|
||||
<include>logback-spring.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<outputDirectory>${project.build.directory}/app</outputDirectory>
|
||||
<outputDirectory>${project.build.directory}/app/${config-path}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@@ -35,7 +35,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
|
||||
import top.continew.starter.extension.crud.annotation.EnableCrudRestController;
|
||||
import top.continew.starter.web.annotation.EnableGlobalExceptionHandler;
|
||||
import top.continew.starter.web.annotation.EnableGlobalResponse;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 启动程序
|
||||
@@ -44,13 +45,13 @@ import top.continew.starter.web.annotation.EnableGlobalExceptionHandler;
|
||||
* @since 2022/12/8 23:15
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@EnableFileStorage
|
||||
@EnableMethodCache(basePackages = "top.continew.admin")
|
||||
@EnableGlobalResponse
|
||||
@EnableCrudRestController
|
||||
@RestController
|
||||
@SpringBootApplication
|
||||
@RequiredArgsConstructor
|
||||
@EnableCrudRestController
|
||||
@EnableGlobalExceptionHandler
|
||||
@EnableMethodCache(basePackages = "top.continew.admin")
|
||||
public class ContiNewAdminApplication implements ApplicationRunner {
|
||||
|
||||
private final ProjectProperties projectProperties;
|
||||
@@ -60,16 +61,11 @@ public class ContiNewAdminApplication implements ApplicationRunner {
|
||||
SpringApplication.run(ContiNewAdminApplication.class, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 访问首页提示
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Hidden
|
||||
@SaIgnore
|
||||
@GetMapping("/")
|
||||
public String index() {
|
||||
return "%s service started successfully.".formatted(projectProperties.getName());
|
||||
public R index() {
|
||||
return R.ok("%s service started successfully.".formatted(projectProperties.getName()), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -21,7 +21,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import top.continew.admin.system.mapper.LogMapper;
|
||||
import top.continew.admin.system.service.UserService;
|
||||
import top.continew.starter.log.core.dao.LogDao;
|
||||
import top.continew.starter.log.httptracepro.autoconfigure.ConditionalOnEnabledLog;
|
||||
import top.continew.starter.log.interceptor.autoconfigure.ConditionalOnEnabledLog;
|
||||
import top.continew.starter.web.autoconfigure.trace.TraceProperties;
|
||||
|
||||
/**
|
||||
|
@@ -22,6 +22,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -43,7 +44,6 @@ import top.continew.starter.log.core.model.LogResponse;
|
||||
import top.continew.starter.web.autoconfigure.trace.TraceProperties;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Map;
|
||||
@@ -66,63 +66,101 @@ public class LogDaoLocalImpl implements LogDao {
|
||||
@Override
|
||||
public void add(LogRecord logRecord) {
|
||||
LogDO logDO = new LogDO();
|
||||
logDO.setDescription(logRecord.getDescription());
|
||||
String module = logRecord.getModule();
|
||||
logDO.setModule(StrUtils.blankToDefault(module, null, m -> m.replace("API", StringConstants.EMPTY).trim()));
|
||||
logDO.setCreateTime(LocalDateTime.ofInstant(logRecord.getTimestamp(), ZoneId.systemDefault()));
|
||||
logDO.setTimeTaken(logRecord.getTimeTaken().toMillis());
|
||||
// 请求信息
|
||||
// 设置请求信息
|
||||
LogRequest logRequest = logRecord.getRequest();
|
||||
this.setRequest(logDO, logRequest);
|
||||
// 设置响应信息
|
||||
LogResponse logResponse = logRecord.getResponse();
|
||||
this.setResponse(logDO, logResponse);
|
||||
// 设置基本信息
|
||||
logDO.setDescription(logRecord.getDescription());
|
||||
logDO.setModule(StrUtils.blankToDefault(logRecord.getModule(), null, m -> m
|
||||
.replace("API", StringConstants.EMPTY)
|
||||
.trim()));
|
||||
logDO.setTimeTaken(logRecord.getTimeTaken().toMillis());
|
||||
logDO.setCreateTime(LocalDateTime.ofInstant(logRecord.getTimestamp(), ZoneId.systemDefault()));
|
||||
// 设置操作人
|
||||
this.setCreateUser(logDO, logRequest, logResponse);
|
||||
logMapper.insert(logDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求信息
|
||||
*
|
||||
* @param logDO 日志信息
|
||||
* @param logRequest 请求信息
|
||||
*/
|
||||
private void setRequest(LogDO logDO, LogRequest logRequest) {
|
||||
logDO.setRequestMethod(logRequest.getMethod());
|
||||
URI requestUrl = logRequest.getUrl();
|
||||
String requestUri = requestUrl.getPath();
|
||||
logDO.setRequestUrl(requestUrl.toString());
|
||||
Map<String, String> requestHeaderMap = logRequest.getHeaders();
|
||||
logDO.setRequestHeaders(JSONUtil.toJsonStr(requestHeaderMap));
|
||||
String requestBody = logRequest.getBody();
|
||||
logDO.setRequestBody(requestBody);
|
||||
logDO.setRequestUrl(logRequest.getUrl().toString());
|
||||
logDO.setRequestHeaders(JSONUtil.toJsonStr(logRequest.getHeaders()));
|
||||
logDO.setRequestBody(logRequest.getBody());
|
||||
logDO.setIp(logRequest.getIp());
|
||||
logDO.setAddress(logRequest.getAddress());
|
||||
logDO.setBrowser(logRequest.getBrowser());
|
||||
logDO.setOs(StrUtil.subBefore(logRequest.getOs(), " or", false));
|
||||
// 响应信息
|
||||
LogResponse logResponse = logRecord.getResponse();
|
||||
Integer statusCode = logResponse.getStatus();
|
||||
logDO.setStatusCode(statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应信息
|
||||
*
|
||||
* @param logDO 日志信息
|
||||
* @param logResponse 响应信息
|
||||
*/
|
||||
private void setResponse(LogDO logDO, LogResponse logResponse) {
|
||||
Map<String, String> responseHeaders = logResponse.getHeaders();
|
||||
logDO.setResponseHeaders(JSONUtil.toJsonStr(responseHeaders));
|
||||
logDO.setTraceId(responseHeaders.get(traceProperties.getHeaderName()));
|
||||
logDO.setTraceId(responseHeaders.get(traceProperties.getTraceIdName()));
|
||||
String responseBody = logResponse.getBody();
|
||||
logDO.setResponseBody(responseBody);
|
||||
// 状态
|
||||
Integer statusCode = logResponse.getStatus();
|
||||
logDO.setStatusCode(statusCode);
|
||||
logDO.setStatus(statusCode >= HttpStatus.HTTP_BAD_REQUEST ? LogStatusEnum.FAILURE : LogStatusEnum.SUCCESS);
|
||||
if (StrUtil.isNotBlank(responseBody) && JSONUtil.isTypeJSON(responseBody)) {
|
||||
if (StrUtil.isNotBlank(responseBody)) {
|
||||
R result = JSONUtil.toBean(responseBody, R.class);
|
||||
if (!result.isSuccess()) {
|
||||
logDO.setStatus(LogStatusEnum.FAILURE);
|
||||
logDO.setErrorMsg(result.getMsg());
|
||||
}
|
||||
// 操作人
|
||||
if (requestUri.startsWith(SysConstants.LOGOUT_URI)) {
|
||||
Long loginId = Convert.toLong(result.getData(), -1L);
|
||||
logDO.setCreateUser(-1 != loginId ? loginId : null);
|
||||
} else if (result.isSuccess() && requestUri.startsWith(SysConstants.LOGIN_URI)) {
|
||||
AccountLoginReq loginReq = JSONUtil.toBean(requestBody, AccountLoginReq.class);
|
||||
logDO.setCreateUser(ExceptionUtils.exToNull(() -> userService.getByUsername(loginReq.getUsername())
|
||||
.getId()));
|
||||
}
|
||||
}
|
||||
// 操作人
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置操作人
|
||||
*
|
||||
* @param logDO 日志信息
|
||||
* @param logRequest 请求信息
|
||||
* @param logResponse 响应信息
|
||||
*/
|
||||
private void setCreateUser(LogDO logDO, LogRequest logRequest, LogResponse logResponse) {
|
||||
String requestUri = URLUtil.getPath(logDO.getRequestUrl());
|
||||
// 解析退出接口信息
|
||||
String responseBody = logResponse.getBody();
|
||||
if (requestUri.startsWith(SysConstants.LOGOUT_URI) && StrUtil.isNotBlank(responseBody)) {
|
||||
R result = JSONUtil.toBean(responseBody, R.class);
|
||||
logDO.setCreateUser(Convert.toLong(result.getData(), null));
|
||||
return;
|
||||
}
|
||||
// 解析登录接口信息
|
||||
if (requestUri.startsWith(SysConstants.LOGIN_URI) && LogStatusEnum.SUCCESS.equals(logDO.getStatus())) {
|
||||
String requestBody = logRequest.getBody();
|
||||
AccountLoginReq loginReq = JSONUtil.toBean(requestBody, AccountLoginReq.class);
|
||||
logDO.setCreateUser(ExceptionUtils.exToNull(() -> userService.getByUsername(loginReq.getUsername())
|
||||
.getId()));
|
||||
return;
|
||||
}
|
||||
// 解析 Token 信息
|
||||
Map<String, String> requestHeaders = logRequest.getHeaders();
|
||||
String headerName = HttpHeaders.AUTHORIZATION;
|
||||
boolean isContains = CollUtil.containsAny(requestHeaderMap.keySet(), Set.of(headerName, headerName
|
||||
boolean isContainsAuthHeader = CollUtil.containsAny(requestHeaders.keySet(), Set.of(headerName, headerName
|
||||
.toLowerCase()));
|
||||
if (!requestUri.startsWith(SysConstants.LOGOUT_URI) && MapUtil.isNotEmpty(requestHeaderMap) && isContains) {
|
||||
String authorization = requestHeaderMap.getOrDefault(headerName, requestHeaderMap.get(headerName
|
||||
if (MapUtil.isNotEmpty(requestHeaders) && isContainsAuthHeader) {
|
||||
String authorization = requestHeaders.getOrDefault(headerName, requestHeaders.get(headerName
|
||||
.toLowerCase()));
|
||||
String token = authorization.replace(SaManager.getConfig()
|
||||
.getTokenPrefix() + StringConstants.SPACE, StringConstants.EMPTY);
|
||||
logDO.setCreateUser(Convert.toLong(StpUtil.getLoginIdByToken(token)));
|
||||
}
|
||||
logMapper.insert(logDO);
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,6 @@ import top.continew.admin.system.service.UserService;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.util.ExceptionUtils;
|
||||
import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.web.model.R;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
|
||||
import java.util.List;
|
||||
@@ -69,7 +68,7 @@ public class AuthController {
|
||||
@SaIgnore
|
||||
@Operation(summary = "账号登录", description = "根据账号和密码进行登录认证")
|
||||
@PostMapping("/account")
|
||||
public R<LoginResp> accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
|
||||
public LoginResp accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid();
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
|
||||
@@ -79,13 +78,13 @@ public class AuthController {
|
||||
String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(loginReq.getPassword()));
|
||||
ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
|
||||
String token = loginService.accountLogin(loginReq.getUsername(), rawPassword, request);
|
||||
return R.ok(LoginResp.builder().token(token).build());
|
||||
return LoginResp.builder().token(token).build();
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@Operation(summary = "手机号登录", description = "根据手机号和验证码进行登录认证")
|
||||
@PostMapping("/phone")
|
||||
public R<LoginResp> phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) {
|
||||
public LoginResp phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) {
|
||||
String phone = loginReq.getPhone();
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
@@ -93,13 +92,13 @@ public class AuthController {
|
||||
ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR);
|
||||
RedisUtils.delete(captchaKey);
|
||||
String token = loginService.phoneLogin(phone);
|
||||
return R.ok(LoginResp.builder().token(token).build());
|
||||
return LoginResp.builder().token(token).build();
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@Operation(summary = "邮箱登录", description = "根据邮箱和验证码进行登录认证")
|
||||
@PostMapping("/email")
|
||||
public R<LoginResp> emailLogin(@Validated @RequestBody EmailLoginReq loginReq) {
|
||||
public LoginResp emailLogin(@Validated @RequestBody EmailLoginReq loginReq) {
|
||||
String email = loginReq.getEmail();
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email;
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
@@ -107,35 +106,35 @@ public class AuthController {
|
||||
ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR);
|
||||
RedisUtils.delete(captchaKey);
|
||||
String token = loginService.emailLogin(email);
|
||||
return R.ok(LoginResp.builder().token(token).build());
|
||||
return LoginResp.builder().token(token).build();
|
||||
}
|
||||
|
||||
@Operation(summary = "用户退出", description = "注销用户的当前登录")
|
||||
@Parameter(name = "Authorization", description = "令牌", required = true, example = "Bearer xxxx-xxxx-xxxx-xxxx", in = ParameterIn.HEADER)
|
||||
@PostMapping("/logout")
|
||||
public R<Object> logout() {
|
||||
public Object logout() {
|
||||
Object loginId = StpUtil.getLoginId(-1L);
|
||||
StpUtil.logout();
|
||||
return R.ok(loginId);
|
||||
return loginId;
|
||||
}
|
||||
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "获取用户信息", description = "获取登录用户信息")
|
||||
@GetMapping("/user/info")
|
||||
public R<UserInfoResp> getUserInfo() {
|
||||
public UserInfoResp getUserInfo() {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
UserDetailResp userDetailResp = userService.get(loginUser.getId());
|
||||
UserInfoResp userInfoResp = BeanUtil.copyProperties(userDetailResp, UserInfoResp.class);
|
||||
userInfoResp.setPermissions(loginUser.getPermissions());
|
||||
userInfoResp.setRoles(loginUser.getRoleCodes());
|
||||
userInfoResp.setPwdExpired(loginUser.isPasswordExpired());
|
||||
return R.ok(userInfoResp);
|
||||
return userInfoResp;
|
||||
}
|
||||
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "获取路由信息", description = "获取登录用户的路由信息")
|
||||
@GetMapping("/route")
|
||||
public R<List<RouteResp>> listRoute() {
|
||||
return R.ok(loginService.buildRouteTree(LoginHelper.getUserId()));
|
||||
public List<RouteResp> listRoute() {
|
||||
return loginService.buildRouteTree(LoginHelper.getUserId());
|
||||
}
|
||||
}
|
@@ -36,7 +36,6 @@ import top.continew.admin.auth.service.LoginService;
|
||||
import top.continew.starter.core.exception.BadRequestException;
|
||||
import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 三方账号认证 API
|
||||
@@ -58,17 +57,17 @@ public class SocialAuthController {
|
||||
@Operation(summary = "三方账号登录授权", description = "三方账号登录授权")
|
||||
@Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
|
||||
@GetMapping("/{source}")
|
||||
public R<SocialAuthAuthorizeResp> authorize(@PathVariable String source) {
|
||||
public SocialAuthAuthorizeResp authorize(@PathVariable String source) {
|
||||
AuthRequest authRequest = this.getAuthRequest(source);
|
||||
return R.ok(SocialAuthAuthorizeResp.builder()
|
||||
return SocialAuthAuthorizeResp.builder()
|
||||
.authorizeUrl(authRequest.authorize(AuthStateUtils.createState()))
|
||||
.build());
|
||||
.build();
|
||||
}
|
||||
|
||||
@Operation(summary = "三方账号登录", description = "三方账号登录")
|
||||
@Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
|
||||
@PostMapping("/{source}")
|
||||
public R<LoginResp> login(@PathVariable String source, @RequestBody AuthCallback callback) {
|
||||
public LoginResp login(@PathVariable String source, @RequestBody AuthCallback callback) {
|
||||
if (StpUtil.isLogin()) {
|
||||
StpUtil.logout();
|
||||
}
|
||||
@@ -77,7 +76,7 @@ public class SocialAuthController {
|
||||
ValidationUtils.throwIf(!response.ok(), response.getMsg());
|
||||
AuthUser authUser = response.getData();
|
||||
String token = loginService.socialLogin(authUser);
|
||||
return R.ok(LoginResp.builder().token(token).build());
|
||||
return LoginResp.builder().token(token).build();
|
||||
}
|
||||
|
||||
private AuthRequest getAuthRequest(String source) {
|
||||
|
@@ -90,33 +90,33 @@ public class CaptchaController {
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "获取行为验证码", description = "获取行为验证码(Base64编码)")
|
||||
@GetMapping("/behavior")
|
||||
public R<Object> getBehaviorCaptcha(CaptchaVO captchaReq, HttpServletRequest request) {
|
||||
public Object getBehaviorCaptcha(CaptchaVO captchaReq, HttpServletRequest request) {
|
||||
captchaReq.setBrowserInfo(JakartaServletUtil.getClientIP(request) + request.getHeader(HttpHeaders.USER_AGENT));
|
||||
ResponseModel responseModel = behaviorCaptchaService.get(captchaReq);
|
||||
CheckUtils.throwIf(() -> !StrUtil.equals(RepCodeEnum.SUCCESS.getCode(), responseModel
|
||||
.getRepCode()), responseModel.getRepMsg());
|
||||
return R.ok(responseModel.getRepData());
|
||||
return responseModel.getRepData();
|
||||
}
|
||||
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "校验行为验证码", description = "校验行为验证码")
|
||||
@PostMapping("/behavior")
|
||||
public R<Object> checkBehaviorCaptcha(@RequestBody CaptchaVO captchaReq, HttpServletRequest request) {
|
||||
public Object checkBehaviorCaptcha(@RequestBody CaptchaVO captchaReq, HttpServletRequest request) {
|
||||
captchaReq.setBrowserInfo(JakartaServletUtil.getClientIP(request) + request.getHeader(HttpHeaders.USER_AGENT));
|
||||
return R.ok(behaviorCaptchaService.check(captchaReq));
|
||||
return behaviorCaptchaService.check(captchaReq);
|
||||
}
|
||||
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "获取图片验证码", description = "获取图片验证码(Base64编码,带图片格式:data:image/gif;base64)")
|
||||
@GetMapping("/image")
|
||||
public R<CaptchaResp> getImageCaptcha() {
|
||||
public CaptchaResp getImageCaptcha() {
|
||||
String uuid = IdUtil.fastUUID();
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid;
|
||||
Captcha captcha = graphicCaptchaService.getCaptcha();
|
||||
long expireTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now()
|
||||
.plusMinutes(captchaProperties.getExpirationInMinutes()));
|
||||
RedisUtils.set(captchaKey, captcha.text(), Duration.ofMinutes(captchaProperties.getExpirationInMinutes()));
|
||||
return R.ok(CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build());
|
||||
return CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +140,7 @@ public class CaptchaController {
|
||||
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
|
||||
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#email", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
|
||||
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#email", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")})
|
||||
public R<Void> getMailCaptcha(@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email) throws MessagingException {
|
||||
public R getMailCaptcha(@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email) throws MessagingException {
|
||||
// 生成验证码
|
||||
CaptchaProperties.CaptchaMail captchaMail = captchaProperties.getMail();
|
||||
String captcha = RandomUtil.randomNumbers(captchaMail.getLength());
|
||||
@@ -182,8 +182,8 @@ public class CaptchaController {
|
||||
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
|
||||
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
|
||||
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")})
|
||||
public R<Void> getSmsCaptcha(@NotBlank(message = "手机号不能为空") @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") String phone,
|
||||
CaptchaVO captchaReq) {
|
||||
public R getSmsCaptcha(@NotBlank(message = "手机号不能为空") @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") String phone,
|
||||
CaptchaVO captchaReq) {
|
||||
// 行为验证码校验
|
||||
ResponseModel verificationRes = behaviorCaptchaService.verification(captchaReq);
|
||||
ValidationUtils.throwIfNotEqual(verificationRes.getRepCode(), RepCodeEnum.SUCCESS.getCode(), verificationRes
|
||||
|
@@ -43,7 +43,6 @@ import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -71,49 +70,50 @@ public class CommonController {
|
||||
|
||||
@Operation(summary = "上传文件", description = "上传文件")
|
||||
@PostMapping("/file")
|
||||
public R<FileUploadResp> upload(@NotNull(message = "文件不能为空") MultipartFile file) {
|
||||
public FileUploadResp upload(@NotNull(message = "文件不能为空") MultipartFile file) {
|
||||
// TODO 实际开发时请删除本条校验
|
||||
ValidationUtils.throwIf(projectProperties.isProduction(), "演示环境不支持上传文件");
|
||||
ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
|
||||
FileInfo fileInfo = fileService.upload(file);
|
||||
return R.ok(FileUploadResp.builder().url(fileInfo.getUrl()).build());
|
||||
return FileUploadResp.builder().url(fileInfo.getUrl()).build();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询部门树", description = "查询树结构的部门列表")
|
||||
@GetMapping("/tree/dept")
|
||||
public R<List<Tree<Long>>> listDeptTree(DeptQuery query, SortQuery sortQuery) {
|
||||
return R.ok(deptService.tree(query, sortQuery, true));
|
||||
public List<Tree<Long>> listDeptTree(DeptQuery query, SortQuery sortQuery) {
|
||||
return deptService.tree(query, sortQuery, true);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询菜单树", description = "查询树结构的菜单列表")
|
||||
@GetMapping("/tree/menu")
|
||||
public R<List<Tree<Long>>> listMenuTree(MenuQuery query, SortQuery sortQuery) {
|
||||
return R.ok(menuService.tree(query, sortQuery, true));
|
||||
public List<Tree<Long>> listMenuTree(MenuQuery query, SortQuery sortQuery) {
|
||||
return menuService.tree(query, sortQuery, true);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询角色字典", description = "查询角色字典列表")
|
||||
@GetMapping("/dict/role")
|
||||
public R<List<LabelValueResp>> listRoleDict(RoleQuery query, SortQuery sortQuery) {
|
||||
return R.ok(roleService.listDict(query, sortQuery));
|
||||
public List<LabelValueResp> listRoleDict(RoleQuery query, SortQuery sortQuery) {
|
||||
return roleService.listDict(query, sortQuery);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询字典", description = "查询字典列表")
|
||||
@Parameter(name = "code", description = "字典编码", example = "notice_type", in = ParameterIn.PATH)
|
||||
@GetMapping("/dict/{code}")
|
||||
public R<List<LabelValueResp>> listDict(@PathVariable String code) {
|
||||
return R.ok(dictItemService.listByDictCode(code));
|
||||
public List<LabelValueResp> listDict(@PathVariable String code) {
|
||||
return dictItemService.listByDictCode(code);
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@Operation(summary = "查询参数字典", description = "查询参数字典")
|
||||
@GetMapping("/dict/option")
|
||||
@Cached(key = "#category", name = CacheConstants.OPTION_KEY_PREFIX)
|
||||
public R<List<LabelValueResp<String>>> listOptionDict(@NotBlank(message = "类别不能为空") @RequestParam String category) {
|
||||
public List<LabelValueResp<String>> listOptionDict(@NotBlank(message = "类别不能为空") String category) {
|
||||
OptionQuery optionQuery = new OptionQuery();
|
||||
optionQuery.setCategory(category);
|
||||
return R.ok(optionService.list(optionQuery)
|
||||
return optionService.list(optionQuery)
|
||||
.stream()
|
||||
.map(option -> new LabelValueResp<>(option.getCode(), StrUtil.nullToDefault(option.getValue(), option
|
||||
.getDefaultValue())))
|
||||
.toList());
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,6 @@ import top.continew.admin.system.model.resp.DashboardTotalResp;
|
||||
import top.continew.admin.system.service.DashboardService;
|
||||
import top.continew.admin.system.model.resp.DashboardNoticeResp;
|
||||
import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.web.model.R;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
|
||||
import java.util.List;
|
||||
@@ -61,8 +60,8 @@ public class DashboardController {
|
||||
|
||||
@Operation(summary = "查询总计信息", description = "查询总计信息")
|
||||
@GetMapping("/total")
|
||||
public R<DashboardTotalResp> getTotal() {
|
||||
return R.ok(dashboardService.getTotal());
|
||||
public DashboardTotalResp getTotal() {
|
||||
return dashboardService.getTotal();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询访问趋势信息", description = "查询访问趋势信息")
|
||||
@@ -71,26 +70,26 @@ public class DashboardController {
|
||||
@CachePenetrationProtect
|
||||
@CacheRefresh(refresh = 7200)
|
||||
@Cached(key = "#days", name = CacheConstants.DASHBOARD_KEY_PREFIX, cacheType = CacheType.BOTH, syncLocal = true)
|
||||
public R<List<DashboardAccessTrendResp>> listAccessTrend(@PathVariable Integer days) {
|
||||
public List<DashboardAccessTrendResp> listAccessTrend(@PathVariable Integer days) {
|
||||
ValidationUtils.throwIf(7 != days && 30 != days, "仅支持查询近 7/30 天访问趋势信息");
|
||||
return R.ok(dashboardService.listAccessTrend(days));
|
||||
return dashboardService.listAccessTrend(days);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询热门模块列表", description = "查询热门模块列表")
|
||||
@GetMapping("/popular/module")
|
||||
public R<List<DashboardPopularModuleResp>> listPopularModule() {
|
||||
return R.ok(dashboardService.listPopularModule());
|
||||
public List<DashboardPopularModuleResp> listPopularModule() {
|
||||
return dashboardService.listPopularModule();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询访客地域分布信息", description = "查询访客地域分布信息")
|
||||
@GetMapping("/geo/distribution")
|
||||
public R<DashboardGeoDistributionResp> getGeoDistribution() {
|
||||
return R.ok(dashboardService.getGeoDistribution());
|
||||
public DashboardGeoDistributionResp getGeoDistribution() {
|
||||
return dashboardService.getGeoDistribution();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询公告列表", description = "查询公告列表")
|
||||
@GetMapping("/notice")
|
||||
public R<List<DashboardNoticeResp>> listNotice() {
|
||||
return R.ok(dashboardService.listNotice());
|
||||
public List<DashboardNoticeResp> listNotice() {
|
||||
return dashboardService.listNotice();
|
||||
}
|
||||
}
|
||||
|
@@ -35,7 +35,6 @@ import top.continew.admin.auth.service.OnlineUserService;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 在线用户 API
|
||||
@@ -54,18 +53,17 @@ public class OnlineUserController {
|
||||
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||
@SaCheckPermission("monitor:online:list")
|
||||
@GetMapping
|
||||
public R<PageResp<OnlineUserResp>> page(OnlineUserQuery query, @Validated PageQuery pageQuery) {
|
||||
return R.ok(baseService.page(query, pageQuery));
|
||||
public PageResp<OnlineUserResp> page(OnlineUserQuery query, @Validated PageQuery pageQuery) {
|
||||
return baseService.page(query, pageQuery);
|
||||
}
|
||||
|
||||
@Operation(summary = "强退在线用户", description = "强退在线用户")
|
||||
@Parameter(name = "token", description = "令牌", example = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiTUd6djdyOVFoeHEwdVFqdFAzV3M5YjVJRzh4YjZPSEUifQ.7q7U3ouoN7WPhH2kUEM7vPe5KF3G_qavSG-vRgIxKvE", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("monitor:online:kickout")
|
||||
@DeleteMapping("/{token}")
|
||||
public R<Void> kickout(@PathVariable String token) {
|
||||
public void kickout(@PathVariable String token) {
|
||||
String currentToken = StpUtil.getTokenValue();
|
||||
CheckUtils.throwIfEqual(token, currentToken, "不能强退自己");
|
||||
StpUtil.kickoutByTokenValue(token);
|
||||
return R.ok("强退成功");
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@ import top.continew.admin.job.service.JobService;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -55,54 +54,53 @@ public class JobController {
|
||||
@Operation(summary = "分页查询任务列表", description = "分页查询任务列表")
|
||||
@SaCheckPermission("schedule:job:list")
|
||||
@GetMapping
|
||||
public R<PageResp<JobResp>> page(JobQuery query) {
|
||||
return R.ok(baseService.page(query));
|
||||
public PageResp<JobResp> page(JobQuery query) {
|
||||
return baseService.page(query);
|
||||
}
|
||||
|
||||
@Operation(summary = "新增任务", description = "新增任务")
|
||||
@SaCheckPermission("schedule:job:add")
|
||||
@PostMapping
|
||||
public R<Void> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody JobReq req) {
|
||||
return baseService.add(req) ? R.ok() : R.fail();
|
||||
public void add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody JobReq req) {
|
||||
baseService.add(req);
|
||||
}
|
||||
|
||||
@Operation(summary = "修改任务", description = "修改任务")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("schedule:job:update")
|
||||
@PutMapping("/{id}")
|
||||
public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody JobReq req, @PathVariable Long id) {
|
||||
return baseService.update(req, id) ? R.ok() : R.fail();
|
||||
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody JobReq req, @PathVariable Long id) {
|
||||
baseService.update(req, id);
|
||||
}
|
||||
|
||||
@Operation(summary = "修改任务状态", description = "修改任务状态")
|
||||
@SaCheckPermission("schedule:job:update")
|
||||
@PatchMapping("/{id}/status")
|
||||
public R<Void> updateStatus(@Validated @RequestBody JobStatusReq req, @PathVariable Long id) {
|
||||
return baseService.updateStatus(req, id) ? R.ok() : R.fail();
|
||||
public void updateStatus(@Validated @RequestBody JobStatusReq req, @PathVariable Long id) {
|
||||
baseService.updateStatus(req, id);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除任务", description = "删除任务")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("schedule:job:delete")
|
||||
@DeleteMapping("/{id}")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
return baseService.delete(id) ? R.ok() : R.fail();
|
||||
public void delete(@PathVariable Long id) {
|
||||
baseService.delete(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "执行任务", description = "执行任务")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("schedule:job:trigger")
|
||||
@PostMapping("/trigger/{id}")
|
||||
public R<Void> trigger(@PathVariable Long id) {
|
||||
return baseService.trigger(id) ? R.ok() : R.fail();
|
||||
public void trigger(@PathVariable Long id) {
|
||||
baseService.trigger(id);
|
||||
}
|
||||
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "查询任务分组列表", description = "查询任务分组列表")
|
||||
@SaCheckPermission("schedule:job:list")
|
||||
@GetMapping("/group")
|
||||
public R<List<String>> listGroup() {
|
||||
List<String> groupList = baseService.listGroup();
|
||||
return R.ok(groupList);
|
||||
public List<String> listGroup() {
|
||||
return baseService.listGroup();
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@ import top.continew.admin.job.model.resp.JobLogResp;
|
||||
import top.continew.admin.job.model.resp.JobInstanceResp;
|
||||
import top.continew.admin.job.service.JobLogService;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -55,37 +54,37 @@ public class JobLogController {
|
||||
@Operation(summary = "分页查询任务日志列表", description = "分页查询任务日志列表")
|
||||
@SaCheckPermission("schedule:log:list")
|
||||
@GetMapping
|
||||
public R<PageResp<JobLogResp>> page(JobLogQuery query) {
|
||||
return R.ok(baseService.page(query));
|
||||
public PageResp<JobLogResp> page(JobLogQuery query) {
|
||||
return baseService.page(query);
|
||||
}
|
||||
|
||||
@Operation(summary = "停止任务", description = "停止任务")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("schedule:log:stop")
|
||||
@PostMapping("/stop/{id}")
|
||||
public R<Void> stop(@PathVariable Long id) {
|
||||
return baseService.stop(id) ? R.ok() : R.fail();
|
||||
public void stop(@PathVariable Long id) {
|
||||
baseService.stop(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "重试任务", description = "重试任务")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("schedule:log:retry")
|
||||
@PostMapping("/retry/{id}")
|
||||
public R<Void> retry(@PathVariable Long id) {
|
||||
return baseService.retry(id) ? R.ok() : R.fail();
|
||||
public void retry(@PathVariable Long id) {
|
||||
baseService.retry(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询任务实例列表", description = "查询任务实例列表")
|
||||
@SaCheckPermission("schedule:log:list")
|
||||
@GetMapping("/instance")
|
||||
public R<List<JobInstanceResp>> listInstance(JobInstanceQuery query) {
|
||||
return R.ok(baseService.listInstance(query));
|
||||
public List<JobInstanceResp> listInstance(JobInstanceQuery query) {
|
||||
return baseService.listInstance(query);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询任务实例日志列表", description = "分页查询任务实例日志列表")
|
||||
@SaCheckPermission("schedule:log:list")
|
||||
@GetMapping("/instance/log")
|
||||
public R<JobInstanceLogPageResult> pageInstanceLog(JobInstanceLogQuery query) {
|
||||
return R.ok(baseService.pageInstanceLog(query));
|
||||
public JobInstanceLogPageResult pageInstanceLog(JobInstanceLogQuery query) {
|
||||
return baseService.pageInstanceLog(query);
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,6 @@ import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 文件管理 API
|
||||
@@ -48,7 +47,7 @@ public class FileController extends BaseController<FileService, FileResp, FileRe
|
||||
@Operation(summary = "查询文件资源统计", description = "查询文件资源统计")
|
||||
@SaCheckPermission("system:file:list")
|
||||
@GetMapping("/statistics")
|
||||
public R<FileStatisticsResp> statistics() {
|
||||
return R.ok(baseService.statistics());
|
||||
public FileStatisticsResp statistics() {
|
||||
return baseService.statistics();
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@
|
||||
package top.continew.admin.controller.system;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
@@ -35,7 +36,6 @@ import top.continew.admin.system.service.LogService;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 系统日志 API
|
||||
@@ -54,18 +54,19 @@ public class LogController {
|
||||
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||
@SaCheckPermission("monitor:log:list")
|
||||
@GetMapping
|
||||
public R<PageResp<LogResp>> page(LogQuery query, @Validated PageQuery pageQuery) {
|
||||
return R.ok(baseService.page(query, pageQuery));
|
||||
public PageResp<LogResp> page(LogQuery query, @Validated PageQuery pageQuery) {
|
||||
return baseService.page(query, pageQuery);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询详情", description = "查询详情")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("monitor:log:list")
|
||||
@GetMapping("/{id}")
|
||||
public R<LogDetailResp> get(@PathVariable Long id) {
|
||||
return R.ok(baseService.get(id));
|
||||
public LogDetailResp get(@PathVariable Long id) {
|
||||
return baseService.get(id);
|
||||
}
|
||||
|
||||
@ExcludeFromGracefulResponse
|
||||
@Operation(summary = "导出登录日志", description = "导出登录日志")
|
||||
@SaCheckPermission("monitor:log:export")
|
||||
@GetMapping("/export/login")
|
||||
@@ -73,6 +74,7 @@ public class LogController {
|
||||
baseService.exportLoginLog(query, sortQuery, response);
|
||||
}
|
||||
|
||||
@ExcludeFromGracefulResponse
|
||||
@Operation(summary = "导出操作日志", description = "导出操作日志")
|
||||
@SaCheckPermission("monitor:log:export")
|
||||
@GetMapping("/export/operation")
|
||||
|
@@ -33,8 +33,8 @@ import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
/**
|
||||
* 菜单管理 API
|
||||
@@ -48,15 +48,15 @@ import top.continew.starter.web.model.R;
|
||||
public class MenuController extends BaseController<MenuService, MenuResp, MenuResp, MenuQuery, MenuReq> {
|
||||
|
||||
@Override
|
||||
public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody MenuReq req) {
|
||||
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody MenuReq req) {
|
||||
this.checkPath(req);
|
||||
return super.add(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody MenuReq req, @PathVariable Long id) {
|
||||
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody MenuReq req, @PathVariable Long id) {
|
||||
this.checkPath(req);
|
||||
return super.update(req, id);
|
||||
super.update(req, id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -32,7 +32,6 @@ import top.continew.admin.system.service.MessageUserService;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.log.core.annotation.Log;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -53,32 +52,30 @@ public class MessageController {
|
||||
|
||||
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||
@GetMapping
|
||||
public R<PageResp<MessageResp>> page(MessageQuery query, @Validated PageQuery pageQuery) {
|
||||
public PageResp<MessageResp> page(MessageQuery query, @Validated PageQuery pageQuery) {
|
||||
query.setUserId(LoginHelper.getUserId());
|
||||
return R.ok(baseService.page(query, pageQuery));
|
||||
return baseService.page(query, pageQuery);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除数据", description = "删除数据")
|
||||
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> delete(@PathVariable List<Long> ids) {
|
||||
public void delete(@PathVariable List<Long> ids) {
|
||||
baseService.delete(ids);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "标记已读", description = "将消息标记为已读状态")
|
||||
@Parameter(name = "ids", description = "消息ID列表", example = "1,2", in = ParameterIn.QUERY)
|
||||
@PatchMapping("/read")
|
||||
public R<Void> readMessage(@RequestParam(required = false) List<Long> ids) {
|
||||
public void readMessage(@RequestParam(required = false) List<Long> ids) {
|
||||
messageUserService.readMessage(ids);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(ignore = true)
|
||||
@Operation(summary = "查询未读消息数量", description = "查询当前用户的未读消息数量")
|
||||
@Parameter(name = "isDetail", description = "是否查询详情", example = "true", in = ParameterIn.QUERY)
|
||||
@GetMapping("/unread")
|
||||
public R<MessageUnreadResp> countUnreadMessage(@RequestParam(required = false) Boolean detail) {
|
||||
return R.ok(messageUserService.countUnreadMessageByUserId(LoginHelper.getUserId(), detail));
|
||||
public MessageUnreadResp countUnreadMessage(@RequestParam(required = false) Boolean detail) {
|
||||
return messageUserService.countUnreadMessageByUserId(LoginHelper.getUserId(), detail);
|
||||
}
|
||||
}
|
@@ -30,8 +30,8 @@ import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -47,16 +47,15 @@ import java.time.LocalDateTime;
|
||||
public class NoticeController extends BaseController<NoticeService, NoticeResp, NoticeDetailResp, NoticeQuery, NoticeReq> {
|
||||
|
||||
@Override
|
||||
public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody NoticeReq req) {
|
||||
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody NoticeReq req) {
|
||||
this.checkTime(req);
|
||||
return super.add(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody NoticeReq req,
|
||||
@PathVariable Long id) {
|
||||
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody NoticeReq req, @PathVariable Long id) {
|
||||
this.checkTime(req);
|
||||
return super.update(req, id);
|
||||
super.update(req, id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -28,7 +28,6 @@ import top.continew.admin.system.model.req.OptionReq;
|
||||
import top.continew.admin.system.model.req.OptionResetValueReq;
|
||||
import top.continew.admin.system.model.resp.OptionResp;
|
||||
import top.continew.admin.system.service.OptionService;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -50,23 +49,21 @@ public class OptionController {
|
||||
@Operation(summary = "查询参数列表", description = "查询参数列表")
|
||||
@SaCheckPermission("system:config:list")
|
||||
@GetMapping
|
||||
public R<List<OptionResp>> list(@Validated OptionQuery query) {
|
||||
return R.ok(baseService.list(query));
|
||||
public List<OptionResp> list(@Validated OptionQuery query) {
|
||||
return baseService.list(query);
|
||||
}
|
||||
|
||||
@Operation(summary = "修改参数", description = "修改参数")
|
||||
@SaCheckPermission("system:config:update")
|
||||
@PutMapping
|
||||
public R<Void> update(@Valid @RequestBody List<OptionReq> options) {
|
||||
public void update(@Valid @RequestBody List<OptionReq> options) {
|
||||
baseService.update(options);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Operation(summary = "重置参数", description = "重置参数")
|
||||
@SaCheckPermission("system:config:reset")
|
||||
@PatchMapping("/value")
|
||||
public R<Void> resetValue(@Validated @RequestBody OptionResetValueReq req) {
|
||||
public void resetValue(@Validated @RequestBody OptionResetValueReq req) {
|
||||
baseService.resetValue(req);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
@@ -31,9 +31,9 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import top.continew.admin.common.constant.CacheConstants;
|
||||
import top.continew.admin.system.enums.SocialSourceEnum;
|
||||
import top.continew.admin.common.util.SecureUtils;
|
||||
import top.continew.admin.common.util.helper.LoginHelper;
|
||||
import top.continew.admin.system.enums.SocialSourceEnum;
|
||||
import top.continew.admin.system.model.entity.UserSocialDO;
|
||||
import top.continew.admin.system.model.req.UserBasicInfoUpdateReq;
|
||||
import top.continew.admin.system.model.req.UserEmailUpdateRequest;
|
||||
@@ -46,7 +46,6 @@ import top.continew.admin.system.service.UserSocialService;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.util.ExceptionUtils;
|
||||
import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -72,22 +71,21 @@ public class UserCenterController {
|
||||
|
||||
@Operation(summary = "修改头像", description = "用户修改个人头像")
|
||||
@PostMapping("/avatar")
|
||||
public R<AvatarResp> updateAvatar(@NotNull(message = "头像不能为空") MultipartFile avatarFile) throws IOException {
|
||||
public AvatarResp updateAvatar(@NotNull(message = "头像不能为空") MultipartFile avatarFile) throws IOException {
|
||||
ValidationUtils.throwIf(avatarFile::isEmpty, "头像不能为空");
|
||||
String newAvatar = userService.updateAvatar(avatarFile, LoginHelper.getUserId());
|
||||
return R.ok("修改成功", AvatarResp.builder().avatar(newAvatar).build());
|
||||
return AvatarResp.builder().avatar(newAvatar).build();
|
||||
}
|
||||
|
||||
@Operation(summary = "修改基础信息", description = "修改用户基础信息")
|
||||
@PatchMapping("/basic/info")
|
||||
public R<Void> updateBasicInfo(@Validated @RequestBody UserBasicInfoUpdateReq req) {
|
||||
public void updateBasicInfo(@Validated @RequestBody UserBasicInfoUpdateReq req) {
|
||||
userService.updateBasicInfo(req, LoginHelper.getUserId());
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "修改密码", description = "修改用户登录密码")
|
||||
@PatchMapping("/password")
|
||||
public R<Void> updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) {
|
||||
public void updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) {
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfNull(rawOldPassword, DECRYPT_FAILED);
|
||||
@@ -95,12 +93,11 @@ public class UserCenterController {
|
||||
.getNewPassword()));
|
||||
ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败");
|
||||
userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId());
|
||||
return R.ok("修改成功,请牢记你的新密码");
|
||||
}
|
||||
|
||||
@Operation(summary = "修改手机号", description = "修改手机号")
|
||||
@PatchMapping("/phone")
|
||||
public R<Void> updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) {
|
||||
public void updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) {
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
|
||||
@@ -110,12 +107,11 @@ public class UserCenterController {
|
||||
ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误");
|
||||
RedisUtils.delete(captchaKey);
|
||||
userService.updatePhone(updateReq.getPhone(), rawOldPassword, LoginHelper.getUserId());
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "修改邮箱", description = "修改用户邮箱")
|
||||
@PatchMapping("/email")
|
||||
public R<Void> updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) {
|
||||
public void updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) {
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
|
||||
@@ -125,40 +121,36 @@ public class UserCenterController {
|
||||
ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误");
|
||||
RedisUtils.delete(captchaKey);
|
||||
userService.updateEmail(updateReq.getEmail(), rawOldPassword, LoginHelper.getUserId());
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "查询绑定的三方账号", description = "查询绑定的三方账号")
|
||||
@GetMapping("/social")
|
||||
public R<List<UserSocialBindResp>> listSocialBind() {
|
||||
public List<UserSocialBindResp> listSocialBind() {
|
||||
List<UserSocialDO> userSocialList = userSocialService.listByUserId(LoginHelper.getUserId());
|
||||
List<UserSocialBindResp> userSocialBindList = userSocialList.stream().map(userSocial -> {
|
||||
return userSocialList.stream().map(userSocial -> {
|
||||
String source = userSocial.getSource();
|
||||
UserSocialBindResp userSocialBind = new UserSocialBindResp();
|
||||
userSocialBind.setSource(source);
|
||||
userSocialBind.setDescription(SocialSourceEnum.valueOf(source).getDescription());
|
||||
return userSocialBind;
|
||||
}).toList();
|
||||
return R.ok(userSocialBindList);
|
||||
}
|
||||
|
||||
@Operation(summary = "绑定三方账号", description = "绑定三方账号")
|
||||
@Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
|
||||
@PostMapping("/social/{source}")
|
||||
public R<Void> bindSocial(@PathVariable String source, @RequestBody AuthCallback callback) {
|
||||
public void bindSocial(@PathVariable String source, @RequestBody AuthCallback callback) {
|
||||
AuthRequest authRequest = authRequestFactory.get(source);
|
||||
AuthResponse<AuthUser> response = authRequest.login(callback);
|
||||
ValidationUtils.throwIf(!response.ok(), response.getMsg());
|
||||
AuthUser authUser = response.getData();
|
||||
userSocialService.bind(authUser, LoginHelper.getUserId());
|
||||
return R.ok("绑定成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "解绑三方账号", description = "解绑三方账号")
|
||||
@Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
|
||||
@DeleteMapping("/social/{source}")
|
||||
public R<Void> unbindSocial(@PathVariable String source) {
|
||||
public void unbindSocial(@PathVariable String source) {
|
||||
userSocialService.deleteBySourceAndUserId(source, LoginHelper.getUserId());
|
||||
return R.ok("解绑成功");
|
||||
}
|
||||
}
|
||||
|
@@ -43,8 +43,8 @@ import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -64,7 +64,7 @@ public class UserController extends BaseController<UserService, UserResp, UserDe
|
||||
private final UserService userService;
|
||||
|
||||
@Override
|
||||
public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody UserReq req) {
|
||||
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody UserReq req) {
|
||||
String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword()));
|
||||
ValidationUtils.throwIfNull(rawPassword, "密码解密失败");
|
||||
ValidationUtils.throwIf(!ReUtil
|
||||
@@ -83,38 +83,36 @@ public class UserController extends BaseController<UserService, UserResp, UserDe
|
||||
@Operation(summary = "解析用户导入数据", description = "解析用户导入数据")
|
||||
@SaCheckPermission("system:user:import")
|
||||
@PostMapping(value = "parseImportUser")
|
||||
public R<UserImportParseResp> parseImportUser(@NotNull(message = "文件不能为空") MultipartFile file) {
|
||||
public UserImportParseResp parseImportUser(@NotNull(message = "文件不能为空") MultipartFile file) {
|
||||
ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
|
||||
return R.ok(userService.parseImportUser(file));
|
||||
return userService.parseImportUser(file);
|
||||
}
|
||||
|
||||
@Operation(summary = "导入用户", description = "导入用户")
|
||||
@SaCheckPermission("system:user:import")
|
||||
@PostMapping(value = "import")
|
||||
public R<UserImportResp> importUser(@Validated @RequestBody UserImportReq req) {
|
||||
return R.ok(userService.importUser(req));
|
||||
public UserImportResp importUser(@Validated @RequestBody UserImportReq req) {
|
||||
return userService.importUser(req);
|
||||
}
|
||||
|
||||
@Operation(summary = "重置密码", description = "重置用户登录密码")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("system:user:resetPwd")
|
||||
@PatchMapping("/{id}/password")
|
||||
public R<Void> resetPassword(@Validated @RequestBody UserPasswordResetReq req, @PathVariable Long id) {
|
||||
public void resetPassword(@Validated @RequestBody UserPasswordResetReq req, @PathVariable Long id) {
|
||||
String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getNewPassword()));
|
||||
ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败");
|
||||
ValidationUtils.throwIf(!ReUtil
|
||||
.isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字");
|
||||
req.setNewPassword(rawNewPassword);
|
||||
baseService.resetPassword(req, id);
|
||||
return R.ok("重置密码成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "分配角色", description = "为用户新增或移除角色")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("system:user:updateRole")
|
||||
@PatchMapping("/{id}/role")
|
||||
public R<Void> updateRole(@Validated @RequestBody UserRoleUpdateReq updateReq, @PathVariable Long id) {
|
||||
public void updateRole(@Validated @RequestBody UserRoleUpdateReq updateReq, @PathVariable Long id) {
|
||||
baseService.updateRole(updateReq, id);
|
||||
return R.ok("分配成功");
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -33,9 +32,10 @@ import top.continew.admin.generator.model.req.GenConfigReq;
|
||||
import top.continew.admin.generator.model.resp.GeneratePreviewResp;
|
||||
import top.continew.admin.generator.model.resp.TableResp;
|
||||
import top.continew.admin.generator.service.GeneratorService;
|
||||
import top.continew.admin.system.service.DictService;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.web.model.R;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
@@ -54,12 +54,21 @@ import java.util.List;
|
||||
public class GeneratorController {
|
||||
|
||||
private final GeneratorService baseService;
|
||||
private final DictService dictService;
|
||||
|
||||
@Operation(summary = "分页查询数据表", description = "分页查询数据表")
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@GetMapping("/table")
|
||||
public R<PageResp<TableResp>> pageTable(TableQuery query, @Validated PageQuery pageQuery) throws SQLException {
|
||||
return R.ok(baseService.pageTable(query, pageQuery));
|
||||
public PageResp<TableResp> pageTable(TableQuery query, @Validated PageQuery pageQuery) throws SQLException {
|
||||
return baseService.pageTable(query, pageQuery);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询生成配置信息", description = "查询生成配置信息")
|
||||
@Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@GetMapping("/config/{tableName}")
|
||||
public GenConfigDO getGenConfig(@PathVariable String tableName) throws SQLException {
|
||||
return baseService.getGenConfig(tableName);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询字段配置列表", description = "查询字段配置列表")
|
||||
@@ -67,43 +76,41 @@ public class GeneratorController {
|
||||
@Parameter(name = "requireSync", description = "是否需要同步", example = "false", in = ParameterIn.QUERY)
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@GetMapping("/field/{tableName}")
|
||||
public R<List<FieldConfigDO>> listFieldConfig(@PathVariable String tableName,
|
||||
@RequestParam(required = false, defaultValue = "false") Boolean requireSync) {
|
||||
return R.ok(baseService.listFieldConfig(tableName, requireSync));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询生成配置信息", description = "查询生成配置信息")
|
||||
@Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@GetMapping("/config/{tableName}")
|
||||
public R<GenConfigDO> getGenConfig(@PathVariable String tableName) throws SQLException {
|
||||
return R.ok(baseService.getGenConfig(tableName));
|
||||
public List<FieldConfigDO> listFieldConfig(@PathVariable String tableName,
|
||||
@RequestParam(required = false, defaultValue = "false") Boolean requireSync) {
|
||||
return baseService.listFieldConfig(tableName, requireSync);
|
||||
}
|
||||
|
||||
@Operation(summary = "保存配置信息", description = "保存配置信息")
|
||||
@Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@PostMapping("/config/{tableName}")
|
||||
public R<Void> saveConfig(@Validated @RequestBody GenConfigReq req, @PathVariable String tableName) {
|
||||
public void saveConfig(@Validated @RequestBody GenConfigReq req, @PathVariable String tableName) {
|
||||
baseService.saveConfig(req, tableName);
|
||||
return R.ok("保存成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "生成预览", description = "预览生成代码")
|
||||
@Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@GetMapping("/preview/{tableName}")
|
||||
public R<List<GeneratePreviewResp>> preview(@PathVariable String tableName) {
|
||||
return R.ok(baseService.preview(tableName));
|
||||
public List<GeneratePreviewResp> preview(@PathVariable String tableName) {
|
||||
return baseService.preview(tableName);
|
||||
}
|
||||
|
||||
@Operation(summary = "生成代码", description = "生成代码")
|
||||
@Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@PostMapping("/{tableNames}")
|
||||
public void generate(@PathVariable List<String> tableNames,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
baseService.generate(tableNames, request, response);
|
||||
public void generate(@PathVariable List<String> tableNames, HttpServletResponse response) {
|
||||
baseService.generate(tableNames, response);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询字典", description = "查询字典列表")
|
||||
@SaCheckPermission("tool:generator:list")
|
||||
@GetMapping("/dict")
|
||||
public List<LabelValueResp> listDict() {
|
||||
List<LabelValueResp> dictList = dictService.listDict(null, null);
|
||||
dictList.addAll(dictService.listEnumDict());
|
||||
return dictList;
|
||||
}
|
||||
}
|
||||
|
@@ -5,5 +5,5 @@
|
||||
\____|\___/ |_| |_| \__||_||_| \_| \___| \_/\_/ /_/ \_\\__,_||_| |_| |_||_||_| |_|
|
||||
|
||||
:: ${project.name} :: v${project.version}
|
||||
:: ContiNew Starter :: v2.4.0
|
||||
:: ContiNew Starter :: v2.6.0
|
||||
:: Spring Boot :: v${spring-boot.version}
|
||||
|
@@ -22,7 +22,7 @@ spring.datasource:
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:root}
|
||||
password: ${DB_PWD:123456}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
@@ -161,7 +161,8 @@ continew-starter.log:
|
||||
## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置)
|
||||
logging:
|
||||
level:
|
||||
top.continew: DEBUG
|
||||
top.continew.admin: DEBUG
|
||||
top.continew.starter: DEBUG
|
||||
file:
|
||||
path: ./logs
|
||||
|
||||
|
@@ -9,12 +9,6 @@ generator:
|
||||
## 类型映射
|
||||
typeMappings:
|
||||
MYSQL:
|
||||
Integer:
|
||||
- int
|
||||
- tinyint
|
||||
- smallint
|
||||
- mediumint
|
||||
- integer
|
||||
String:
|
||||
- varchar
|
||||
- char
|
||||
@@ -23,21 +17,27 @@ generator:
|
||||
- longtext
|
||||
- tinytext
|
||||
- json
|
||||
LocalDate:
|
||||
- date
|
||||
LocalDateTime:
|
||||
- datetime
|
||||
- timestamp
|
||||
Integer:
|
||||
- int
|
||||
- tinyint
|
||||
- smallint
|
||||
- mediumint
|
||||
- integer
|
||||
Long:
|
||||
- bigint
|
||||
Float:
|
||||
- float
|
||||
Double:
|
||||
- double
|
||||
BigDecimal:
|
||||
- decimal
|
||||
Boolean:
|
||||
- bit
|
||||
BigDecimal:
|
||||
- decimal
|
||||
LocalDate:
|
||||
- date
|
||||
LocalDateTime:
|
||||
- datetime
|
||||
- timestamp
|
||||
## 模板配置
|
||||
templateConfigs:
|
||||
DO:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user