boyuehasfj-java

This commit is contained in:
luoyu 2025-06-02 21:30:55 +08:00
commit 2d6310f3ae
678 changed files with 78592 additions and 0 deletions

50
.gitignore vendored Normal file
View File

@ -0,0 +1,50 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
*.lck
!*/build/*.java
!*/build/*.html
!*/build/*.xml
effective-pom.xml

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "boyueApplication",
"request": "launch",
"mainClass": "com.boyue.boyueApplication",
"projectName": "boyue-admin"
}
]
}

68
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,68 @@
{
"java.configuration.updateBuildConfiguration": "interactive",
"java.compile.nullAnalysis.mode": "disabled",
"maven.view": "hierarchical",
"maven.executable.options": "-T 4",
"maven.pomfile.autoUpdateEffectivePOM": true,
"java.debug.settings.hotCodeReplace": "auto",
"spring-boot.ls.java.home": "",
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=9 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms4G -Xlog:disable",
"maven.excludedFolders": [
"**/.vscode",
"**/.idea",
"**/target",
"**/.*",
"**/node_modules",
"**/target",
"**/bin",
"**/archetype-resources"
],
"boot-java.rewrite.refactorings.on": true,
"maven.executable.preferMavenWrapper": true,
"java.import.maven.enabled": true,
"java.dependency.packagePresentation": "hierarchical",
"dbcode.connections": [
{
"connectionId": "btr-_nFe7R0oOvCj0mMun",
"name": "ry-mysql",
"driver": "mysql",
"connectionType": "host",
"host": "127.0.0.1",
"port": 3306,
"ssl": false,
"username": "root",
"password": "123456",
"savePassword": "secretStorage",
"database": "ry",
"connectionTimeout": 30,
"driverOptions": {
"retrievePublickey": true
}
},
{
"connectionId": "7NX2UhXl__9t3Ca6TzEsB",
"name": "ry-postgres",
"driver": "postgres",
"connectionType": "host",
"host": "127.0.0.1",
"port": 5432,
"ssl": false,
"username": "postgres",
"password": "123456",
"savePassword": "secretStorage",
"connectionTimeout": 30
},
{
"connectionId": "fNsY4HlOb21w_5TnIGy_d",
"name": "localhost",
"driver": "redis",
"connectionType": "host",
"host": "127.0.0.1",
"port": 6379,
"ssl": false,
"savePassword": "na",
"readOnly": false,
"connectionTimeout": 30
}
],
}

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 boyue
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

214
README.md Normal file
View File

@ -0,0 +1,214 @@
<p align="center">
<span>
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</span>
<span>+</span>
<span>
<img alt="logo" src="./doc/image/logo.png">
</span>
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">boyue-Geek v3.8.9-G</h1>
<h4 align="center">基于SpringBoot3+Vue3前后端分离的Java快速开发框架</h4>
<p align="center">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg">
</p>
# 当前版本是3.8.9-G
本人的其他两个推荐搭配的项目
1. [boyue-App-Geek: 这是若依极客生态的小程序版本 (gitee.com)](https://gitee.com/geek-xd/geek-uniapp-vue3-uview-plus-uchart)
2. [boyue-Vue3-Geek: 这是若依极客生态的Vue3版本 (gitee.com)](https://gitee.com/geek-xd/ruo-yi-vue3-geek)
与本项目同为一个作者开发,兼容性最好,学习成本最低。
# 引言
boyue-Vue与boyue-App是基于SpringBoot2+Vue2打造的企业级开发框架得到了广大开发者的喜爱和积极反馈。随着技术的迭代进步SpringBoot3与Vue3逐渐进入开发者的视野。为了满足开发者对于新技术的追求boyue官方文档提供了SpringBoot2至SpringBoot3的升级方法。与此同时社区也涌现出了boyue-Vue3、boyue-App-Vue3的版本展现了开发者社区对于技术升级的热情与努力。
然而在升级的过程中官方的方法为了兼顾Java1.8的特性与一些老旧的方法未完全拥抱SpringBoot3与Java17的全部特性。而社区的boyue-Vue3、boyue-App-Vue3版本由于出自不同的团队之手兼容性及整合性上存在些许不足。更为关键的是尽管这些版本支持TypeScript但缺乏与之相匹配的tsconfig.json配置文件这使得在主流编辑器如VSCode中TypeScript的语法提示环境并未达到最佳状态。
鉴于此boyue-Geek生态应运而生。它旨在为广大开发者提供一个既保留原版本核心特性又整合社区版优点的全新解决方案。在boyue-Geek中我们深入调研了企业开发中常用的boyue扩展并直接在框架中集成确保开发者能够快速上手高效开发。同时我们采用了最新的SpringBoot3+Vue3技术栈彻底移除了为了兼容Java1.8而保留的老旧方法。更为重要的是我们为TypeScript开发环境加入了常用的tsconfig.json配置使得开发者在VSCode等编辑器中能够获得更为舒适、便捷的语法提示体验。
boyue-Geek不仅仅是一个简单的升级版本更是对于boyue生态的一次全面优化与整合。我们相信通过boyue-Geek开发者将能够更为高效、愉悦地开发出优秀的企业级应用。
## 平台简介
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 前端采用Vue3、Element Plus。
* 后端采用Spring Boot3、Spring Security、Redis & Jwt。
* 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。
* 多数据源与分库分表默认集成
* 所有非基本模块可随意插拔,让开发更加简单高效
* 提供了多个工具模块助力开发在线接口模块、mybatis-jpa模块
* 提供了多个常见业务模块简化开发,如:第三方认证模块、支付模块
* 提供了多个常见的服务模块集成开发websocket模块、minio模块
* 特别鸣谢:[element](https://github.com/ElemeFE/element)[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://github.com/elunez/eladmin-web)。
* 阿里云折扣场:[点我进入](http://aly.boyue.vip),腾讯云秒杀场:[点我进入](http://txy.boyue.vip)&nbsp;&nbsp;
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
## 本项目与原项目的区别
### 核心
**模块化架构设计,支持各个模块的快速安拆,对第三方认证、第三方支付模块设计了基础的规范和基础模块。**
### 细节
1. 改用SpringBoot3+java17的更新的技术栈并改掉所有的弃用的方法全面拥抱java17的全新特性。
2. 升级了代码生成器配合本项目的vue3版本才可用使关联表生成更加简单
3. 改用最新版本的SpringSecurity安全框架以及采用最新的lambda 表达式的配置方式,更加通俗易懂!
4. 自动Api文档以springfox替代springdoc来适配knife4j框架的4.x版本更好的适配springboot3
5. 默认引入mybatis-plus增强mybatis并自创工具模块mybatis-jpa简化CRUD
6. 默认引入lombok简化代码基础模块并未使用mybatis-plus和lombok对这两个扩展有争议的小伙伴可以直接删除不会影响到框架本身滴以及knife4j直接删除也不会影响到springdoc主要还是为了方便咱们开发者呢
7. 提供了大量可随意插拔的模块助力快速开发同时boyue插件集成中的常用插件也以模块的形式直接集成在了项目中也是可以随意插拔的嗷~
## 模块介绍(简单开发必看)
* 最简单的开发就是删除所有的可移除模块,按需添加模块。
* 测试中的模块请自己使用的时候一定要测试一下。
* 对于小白,开发中的模块请直接删除。
```
com.boyue
├── common // 工具类
│ └── annotation // 自定义注解
│ └── config // 全局配置
│ └── constant // 通用常量
│ └── core // 核心控制
│ └── enums // 通用枚举
│ └── exception // 通用异常
│ └── filter // 过滤器处理
│ └── utils // 通用类处理
├── framework // 框架核心
│ └── aspectj // 注解实现
│ └── config // 系统配置
│ └── datasource // 数据权限
│ └── interceptor // 拦截器
│ └── manager // 异步处理
│ └── security // 权限控制
│ └── web // 前端控制
├── boyue-admin // 后台服务
├── boyue-pay // 支付场景(开发中)
│ └── common // 支付框架基础模块(测试中)
│ └── sqb // 收钱吧支付模块(测试中)
│ └── wx // 微信支付模块(待开发)
│ └── alipay // 支付宝支付模块(开发中)
│ └── starter // 支付场景启动器
├── boyue-oauth // 第三方认证场景(测试中)
│ └── common // 第三方认证基础模块(开发中)
│ └── justauth // 网站第三方认证模块测试中参照若依扩展改进因没有这么多场景的code请大家测试出问题后help解决一下发出来
│ └── wx // 微信小程序认证模块(测试中)
│ └── phone // 手机认证模块(测试中)
│ └── email // 邮箱认证模块(开发中)
│ └── starter // 第三方认证启动器
├── boyue-middleware // 中间件场景(可移除)
│ └── minio // minio文件服务模块
│ └── rabbitmq // rabbitmq队列服务模块
│ └── redis // redis缓存服务模块与ehcache插件同类两者二选一
│ └── starter // 中间件整合模块
├── boyue-models // 业务场景
│ └── online // 在线开发模块(可移除)
│ └── quartz // 定时任务(可移除)
│ └── generator // 代码生成(可移除)
│ └── starter // 业务场景启动器
├── boyue-plugins // 插件
│ └── ehcache // ehcache缓存插件与redis模块同类两者二选一
│ └── mybatis-jpa // mybatis-jpa插件可移除简化CRUD以数据模型为基础开发
│ └── mybatis-plus // mybatis-plus插件可移除简化CRUD
│ └── mybatis-interceptor // mybatis-interceptor插件可移除简化数据鉴权和分页扩展性强
│ └── atomikos // atomikos分布式事务插件可移除
│ └── netty // netty插件可移除
│ └── websocket // websocket插件可移除
│ └── starter // 插件整合模块
├── boyue-system // 系统代码
```
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成前后端代码的生成java、html、xml、sql支持CRUD下载 。
14. 系统接口根据业务代码自动生成相关的api接口文档。
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 缓存监控:对系统的缓存信息查询,命令统计等。
17. 在线构建器拖动表单元素生成相应的HTML代码。
18. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
19. 支付场景
20. 第三方登录场景
21. 中间件场景
## 演示图
### 新加功能和增强功能演示
<table>
<tr>
<td><img src="./doc/image/online-mb-list.png"/></td>
<td><img src="./doc/image/online-mb-edit.png"/></td>
</tr>
<tr>
<td><img src="./doc/image/online-mb-code.png"/></td>
<td><img src="./doc/image/form-edit.png"/></td>
</tr>
<tr>
<td><img src="./doc/image/code-edit.png"/></td>
<td><img src="./doc/image/code-show.png"/></td>
</tr>
</table>
### 原有功能演示
<table>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-936ec82d1f4872e1bc980927654b6007307.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr>
</table>
# 联系我们:
QQ交流群744785891

12
bin/clean.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
echo.
echo [信息] 清理工程target生成路径。
echo.
%~d0
cd %~dp0
cd ..
call mvn clean
pause

12
bin/package.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
echo.
echo [信息] 打包Web工程生成war/jar包文件。
echo.
%~d0
cd %~dp0
cd ..
call mvn clean package -Dmaven.test.skip=true
pause

14
bin/run.bat Normal file
View File

@ -0,0 +1,14 @@
@echo off
echo.
echo [<5B><>Ϣ] ʹ<><CAB9>Jar<61><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̡<EFBFBD>
echo.
cd %~dp0
cd ../boyue-admin/target
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -jar %JAVA_OPTS% boyue-admin.jar
cd bin
pause

118
boyue-admin/pom.xml Normal file
View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyuehasfj-java</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>boyue-admin</artifactId>
<description>
web服务入口
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-framework</artifactId>
</dependency>
<!-- 集成第三方登录启动器 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-starter</artifactId>
</dependency>
<!-- 集成第三方支付启动器 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-pay-starter</artifactId>
</dependency>
<!-- 中间件 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-middleware-starter</artifactId>
</dependency>
<!-- plugins-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-plugins-starter</artifactId>
</dependency>
<!-- models-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-models-starter</artifactId>
</dependency>
<!-- file-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-file-starter</artifactId>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@ -0,0 +1,43 @@
package com.boyue;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
/**
* 启动程序
*
* @author boyue
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class },
excludeName = {
// 排除Flowable自动配置
"org.flowable.spring.boot.process.ProcessEngineAutoConfiguration",
"org.flowable.spring.boot.process.ProcessEngineServicesAutoConfiguration",
"org.flowable.spring.boot.app.AppEngineAutoConfiguration",
"org.flowable.spring.boot.app.AppEngineServicesAutoConfiguration"
}
)
public class BoYueApplication {
public static void main(String[] args) throws UnknownHostException {
// System.setProperty("spring.devtools.restart.enabled", "false");
ConfigurableApplicationContext application = SpringApplication.run(BoYueApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 启动成功 ლ(´ڡ`ლ)゙ \n");
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
System.out.println("\n----------------------------------------------------------\n" +
" Application boyue-Geek is running! Access URLs:\n" +
" Local: http://localhost:" + port + "/\n" +
" External: http://" + ip + ":" + port + "/\n" +
" Swagger文档: http://" + ip + ":" + port + "/swagger-ui/index.html\n" +
" Knife4j文档: http://" + ip + ":" + port + "/doc.html" + "" + "\n" +
"----------------------------------------------------------");
}
}

View File

@ -0,0 +1,18 @@
package com.boyue;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* web容器中进行部署
*
* @author boyue
*/
public class BoYueServletInitializer extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(BoYueApplication.class);
}
}

View File

@ -0,0 +1,81 @@
package com.boyue.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.boyue.common.config.BoYueConfig;
import com.boyue.common.constant.CacheConstants;
import com.boyue.common.constant.Constants;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.utils.CacheUtils;
import com.boyue.common.utils.sign.Base64;
import com.boyue.common.utils.uuid.IdUtils;
import com.boyue.system.service.ISysConfigService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
/**
* 验证码操作处理
*
* @author boyue
*/
@RestController
public class CaptchaController {
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private ISysConfigService configService;
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException {
AjaxResult ajax = AjaxResult.success();
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled) {
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID(), capStr = null, code = null;
BufferedImage image = null;
// 生成验证码
String captchaType = BoYueConfig.getCaptchaType();
if ("math".equals(captchaType)) {
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
} else if ("char".equals(captchaType)) {
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
CacheUtils.put(CacheConstants.CAPTCHA_CODE_KEY, uuid, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try {
ImageIO.write(image, "jpg", os);
} catch (IOException e) {
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
}

View File

@ -0,0 +1,376 @@
package com.boyue.web.controller.common;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.multipart.MultipartFile;
import com.boyue.common.annotation.Anonymous;
import com.boyue.common.config.BoYueConfig;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.file.FileUtils;
import com.boyue.common.utils.file.MimeTypeUtils;
import com.boyue.file.utils.FileOperateUtils;
import com.boyue.framework.config.ServerConfig;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 通用请求处理
*
* @author boyue
*/
@Tag(name = "通用请求处理")
@RestController
@RequestMapping("/common")
public class CommonController {
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
private static final String FILE_DELIMETER = ",";
@Autowired
private ServerConfig serverConfig;
/**
* 通用下载请求
*
* @param fileName 文件名称
* @param delete 是否删除
*/
@Operation(summary = "通用下载请求")
@Parameters({
@Parameter(name = "fileName", description = "文件名称"),
@Parameter(name = "delete", description = "是否删除")
})
@GetMapping("/download")
@Anonymous
public void fileDownload(
@RequestParam("fileName") String fileName,
@RequestParam("delete") Boolean delete,
HttpServletResponse response,
HttpServletRequest request) {
try {
if (!FileUtils.checkAllowDownload(fileName)) {
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = BoYueConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
// FileUtils.writeBytes(filePath, response.getOutputStream());
FileOperateUtils.downLoad(filePath, response.getOutputStream());
if (delete) {
FileOperateUtils.deleteFile(fileName);
}
} catch (Exception e) {
log.error("下载文件失败", e);
}
}
/**
* 通用上传请求单个
*/
@Operation(summary = "通用上传请求(单个)")
@PostMapping("/upload")
@Anonymous
public AjaxResult uploadFile(MultipartFile file) throws Exception {
try {
if (file == null) {
return AjaxResult.error("上传的文件不能为空");
}
// 记录文件信息
log.info("接收到文件上传请求,文件名: {}, 大小: {}KB",
file.getOriginalFilename(), file.getSize() / 1024);
String url = FileOperateUtils.upload(file);
log.info("文件上传成功,文件地址: {}", url);
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", getFileName(file.getOriginalFilename()));
ajax.put("newFileName", FileUtils.getName(file.getOriginalFilename()));
ajax.put("originalFilename", file.getOriginalFilename());
ajax.put("code", 200); // 确保返回正确的状态码
return ajax;
} catch (Exception e) {
log.error("文件上传失败", e);
return AjaxResult.error("文件上传失败: " + e.getMessage());
}
}
/**
* 从文件名中提取文件名不带路径
*/
private String getFileName(String name) {
if (name == null) {
return "";
}
int lastIndex = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'));
return lastIndex >= 0 ? name.substring(lastIndex + 1) : name;
}
/**
* 通用上传请求多个
*/
@Operation(summary = "通用上传请求(多个)")
@PostMapping("/uploads")
@Anonymous
public AjaxResult uploadFiles(@RequestBody List<MultipartFile> files)
throws Exception {
try {
// 上传文件路径
String filePath = BoYueConfig.getUploadPath();
List<String> urls = new ArrayList<String>();
List<String> fileNames = new ArrayList<String>();
List<String> newFileNames = new ArrayList<String>();
List<String> originalFilenames = new ArrayList<String>();
for (MultipartFile file : files) {
// 上传并返回新文件名称
String fileName = FileOperateUtils.upload(filePath, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
return ajax;
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
/**
* 本地资源通用下载
*/
@Operation(summary = "本地资源通用下载")
@GetMapping("/download/resource")
@Anonymous
public void resourceDownload(@Parameter(name = "resource", description = "资源名称") String resource,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
try {
if (!FileUtils.checkAllowDownload(resource)) {
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
}
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, resource);
FileOperateUtils.downLoad(resource, response.getOutputStream());
} catch (Exception e) {
log.error("下载文件失败", e);
}
}
/**
* 删除文件请求
*
* @param filePathParam 包含文件路径的参数对象
* @return 删除结果
*/
@Operation(summary = "删除文件请求")
@PostMapping("/deleteFile")
@Anonymous
public AjaxResult deleteFile(@RequestBody(required = false) FilePathParam filePathParam,
@RequestParam(value = "filePath", required = false) String filePathParam2) {
try {
String filePath;
// 支持两种方式传参请求体或URL参数
if (filePathParam != null && StringUtils.isNotEmpty(filePathParam.filePath)) {
filePath = filePathParam.filePath;
} else if (StringUtils.isNotEmpty(filePathParam2)) {
filePath = filePathParam2;
} else {
return AjaxResult.error("文件路径不能为空");
}
log.info("原始文件路径: {}", filePath);
// 处理文件路径格式
if (filePath.contains("http://") || filePath.contains("https://")) {
// 如果是完整URL提取文件名或相对路径部分
if (filePath.contains("/profile/")) {
filePath = filePath.substring(filePath.indexOf("/profile/") + 9);
} else {
// 尝试从路径中提取文件名
filePath = filePath.substring(filePath.lastIndexOf('/') + 1);
}
}
// 处理反斜杠
filePath = filePath.replace('\\', '/');
// 去除开头的斜杠
if (filePath.startsWith("/")) {
filePath = filePath.substring(1);
}
log.info("处理后的文件路径: {}", filePath);
// 安全检查
if (!FileUtils.checkAllowDownload(filePath)) {
return AjaxResult.error(StringUtils.format("文件名称({})非法,不允许删除。", filePath));
}
// 执行删除
boolean result = FileOperateUtils.deleteFile(filePath);
if (result) {
return AjaxResult.success("删除成功");
} else {
return AjaxResult.error("文件删除失败");
}
} catch (Exception e) {
log.error("删除文件失败", e);
return AjaxResult.error("删除文件失败: " + e.getMessage());
}
}
/**
* 删除文件请求 - GET方法
*
* @param filePath 文件路径
* @return 删除结果
*/
@Operation(summary = "删除文件请求 - GET方式")
@GetMapping("/deleteFile")
@Anonymous
public AjaxResult deleteFileGet(@RequestParam("filePath") String filePath) {
FilePathParam param = new FilePathParam();
param.setFilePath(filePath);
return deleteFile(param, null);
}
/**
* 文件路径参数类
*/
public static class FilePathParam {
public String filePath;
public FilePathParam() {
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
/**
* 文件上传诊断工具
*/
@Operation(summary = "文件上传诊断工具")
@GetMapping("/diagnose-upload")
@Anonymous
public AjaxResult diagnoseUpload() {
try {
Map<String, Object> diagnostics = new HashMap<>();
// 获取系统配置
diagnostics.put("uploadPath", BoYueConfig.getProfile());
diagnostics.put("fileServer", BoYueConfig.getFileServer());
// 系统信息
Map<String, Object> systemInfo = new HashMap<>();
systemInfo.put("os.name", System.getProperty("os.name"));
systemInfo.put("file.separator", File.separator);
systemInfo.put("user.dir", System.getProperty("user.dir"));
diagnostics.put("systemInfo", systemInfo);
// 检查目录
List<String> pathsToCheck = new ArrayList<>();
pathsToCheck.add(BoYueConfig.getProfile());
pathsToCheck.add(BoYueConfig.getProfile() + "/files");
pathsToCheck.add(BoYueConfig.getProfile() + "/files/master");
List<Map<String, Object>> directoryChecks = new ArrayList<>();
for (String path : pathsToCheck) {
Map<String, Object> check = new HashMap<>();
File directory = new File(path);
check.put("path", path);
check.put("exists", directory.exists());
check.put("isDirectory", directory.exists() && directory.isDirectory());
check.put("canWrite", directory.exists() && directory.canWrite());
check.put("canRead", directory.exists() && directory.canRead());
check.put("absolutePath", directory.getAbsolutePath());
directoryChecks.add(check);
}
diagnostics.put("directoryChecks", directoryChecks);
// 尝试创建测试文件
try {
String testDirectory = BoYueConfig.getProfile() + "/files/master";
File dir = new File(testDirectory);
if (!dir.exists()) {
dir.mkdirs();
}
String testFilePath = testDirectory + "/test_" + System.currentTimeMillis() + ".txt";
File testFile = new File(testFilePath);
boolean created = testFile.createNewFile();
Map<String, Object> testFileInfo = new HashMap<>();
testFileInfo.put("path", testFilePath);
testFileInfo.put("created", created);
testFileInfo.put("exists", testFile.exists());
if (created && testFile.exists()) {
// 写入一些内容
try (java.io.FileWriter writer = new java.io.FileWriter(testFile)) {
writer.write("Test file created at: " + new java.util.Date());
}
// 生成访问URL
String url = FileOperateUtils.getURL(testFilePath.substring(BoYueConfig.getProfile().length() + 1));
testFileInfo.put("url", url);
// 删除测试文件
boolean deleted = testFile.delete();
testFileInfo.put("deleted", deleted);
}
diagnostics.put("testFileInfo", testFileInfo);
} catch (Exception e) {
Map<String, Object> testFileError = new HashMap<>();
testFileError.put("error", e.getMessage());
testFileError.put("stackTrace", e.getStackTrace());
diagnostics.put("testFileError", testFileError);
}
return AjaxResult.success("诊断完成").put("diagnostics", diagnostics);
} catch (Exception e) {
log.error("诊断失败", e);
return AjaxResult.error("诊断失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,122 @@
package com.boyue.web.controller.monitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.constant.CacheConstants;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.text.Convert;
import com.boyue.common.utils.CacheUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.system.domain.SysCache;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 缓存监控
*
* @author boyue
*/
@Tag(name = "缓存监控")
@RestController
@RequestMapping("/monitor/cache")
public class CacheController {
private static String tmpCacheName = "";
private final static List<SysCache> caches = new ArrayList<SysCache>();
{
caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
caches.add(new SysCache(CacheConstants.PHONE_CODES, "短信验证码"));
caches.add(new SysCache(CacheConstants.EMAIL_CODES, "邮箱验证码"));
caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
caches.add(new SysCache(CacheConstants.IP_ERR_CNT_KEY, "IP错误次数"));
caches.add(new SysCache(CacheConstants.FILE_MD5_PATH_KEY, "path-md5"));
caches.add(new SysCache(CacheConstants.FILE_PATH_MD5_KEY, "md5-path"));
}
@Operation(summary = "获取缓存名列表")
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getNames")
public AjaxResult cache() {
return AjaxResult.success(caches);
}
@Operation(summary = "获取缓存键列表")
@Parameters({
@Parameter(name = "cacheName", description = "缓存名称", required = true),
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getKeys/{cacheName}")
public AjaxResult getCacheKeys(@PathVariable String cacheName) {
tmpCacheName = cacheName;
Set<String> keyset = CacheUtils.getkeys(cacheName);
return AjaxResult.success(keyset);
}
@Operation(summary = "获取缓存值列表")
@Parameters({
@Parameter(name = "cacheName", description = "缓存名称", required = true),
@Parameter(name = "cacheKey", description = "缓存键名", required = true)
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getValue/{cacheName}/{cacheKey}")
public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {
ValueWrapper valueWrapper = CacheUtils.get(cacheName, cacheKey);
SysCache sysCache = new SysCache();
sysCache.setCacheName(cacheName);
sysCache.setCacheKey(cacheKey);
if (StringUtils.isNotNull(valueWrapper)) {
sysCache.setCacheValue(Convert.toStr(valueWrapper.get(), ""));
}
return AjaxResult.success(sysCache);
}
@Operation(summary = "清除缓存")
@Parameters({
@Parameter(name = "cacheName", description = "缓存名称", required = true)
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheName/{cacheName}")
public AjaxResult clearCacheName(@PathVariable String cacheName) {
CacheUtils.clear(cacheName);
return AjaxResult.success();
}
@Operation(summary = "清除缓存值")
@Parameters({
@Parameter(name = "cacheKey", description = "缓存键名", required = true)
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheKey/{cacheKey}")
public AjaxResult clearCacheKey(@PathVariable String cacheKey) {
CacheUtils.removeIfPresent(tmpCacheName, cacheKey);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheAll")
public AjaxResult clearCacheAll() {
for (String cacheName : CacheUtils.getCacheManager().getCacheNames()) {
CacheUtils.clear(cacheName);
}
return AjaxResult.success();
}
}

View File

@ -0,0 +1,32 @@
package com.boyue.web.controller.monitor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.framework.web.domain.Server;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 服务器监控
*
* @author boyue
*/
@Tag(name = "服务器监控")
@RestController
@RequestMapping("/monitor/server")
public class ServerController {
@Operation(summary = "获取服务器监控信息")
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception {
Server server = new Server();
server.copyTo();
return AjaxResult.success(server);
}
}

View File

@ -0,0 +1,96 @@
package com.boyue.web.controller.monitor;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.framework.web.service.SysPasswordService;
import com.boyue.system.domain.SysLogininfor;
import com.boyue.system.service.ISysLogininforService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 系统访问记录
*
* @author boyue
*/
@Tag(name = "系统访问记录")
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController {
@Autowired
private ISysLogininforService logininforService;
@Autowired
private SysPasswordService passwordService;
@Operation(summary = "获取系统访问记录列表")
@PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
@GetMapping("/list")
public TableDataInfo list(SysLogininfor logininfor) {
startPage();
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
return getDataTable(list);
}
@Operation(summary = "导出系统访问记录列表")
@Log(title = "登录日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysLogininfor logininfor) {
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
util.exportExcel(response, list, "登录日志");
}
@Operation(summary = "删除系统访问记录")
@Parameters({
@Parameter(name = "infoIds", description = "记录id数组", required = true),
})
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoIds}")
public AjaxResult remove(@PathVariable(name = "infoIds") Long[] infoIds) {
return toAjax(logininforService.deleteLogininforByIds(infoIds));
}
@Operation(summary = "清除系统访问记录")
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean")
public AjaxResult clean() {
logininforService.cleanLogininfor();
return success();
}
@Operation(summary = "账户解锁")
@Parameters({
@Parameter(name = "userName", description = "用户名", required = true),
})
@PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
@Log(title = "账户解锁", businessType = BusinessType.OTHER)
@GetMapping("/unlock/{userName}")
public AjaxResult unlock(@PathVariable("userName") String userName) {
passwordService.clearLoginRecordCache(userName);
return success();
}
}

View File

@ -0,0 +1,102 @@
package com.boyue.web.controller.monitor;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.R;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.system.domain.SysOperLog;
import com.boyue.system.service.ISysOperLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 操作日志记录
*
* @author boyue
*/
@Tag(name = "操作日志记录")
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController {
@Autowired
private ISysOperLogService operLogService;
@Operation(summary = "获取操作日志记录列表")
@PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
@GetMapping("/list")
public TableDataInfo list(SysOperLog operLog) {
startPage();
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
return getDataTable(list);
}
@Operation(summary = "导出操作日志记录列表")
@Log(title = "操作日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysOperLog operLog) {
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
util.exportExcel(response, list, "操作日志");
}
@Operation(summary = "删除操作日志记录")
@Parameters({
@Parameter(name = "operIds", description = "记录id数组", required = true),
})
@Log(title = "操作日志", businessType = BusinessType.DELETE)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable(name = "operIds") Long[] operIds) {
return toAjax(operLogService.deleteOperLogByIds(operIds));
}
@Operation(summary = "清除操作日志记录")
@Log(title = "操作日志", businessType = BusinessType.CLEAN)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/clean")
public AjaxResult clean() {
operLogService.cleanOperLog();
return success();
}
@Operation(summary = "业务监控")
@GetMapping("/business")
public R<Map<String, Object>> business(SysOperLog operLog) {
// 查询并获取统计数据
List<Map<String, Object>> successStats = operLogService.getSuccessOperationStats(operLog);
List<Map<String, Object>> failureStats = operLogService.getFailureOperationStats(operLog);
List<Map<String, Object>> statusStats = operLogService.getStatusStats(operLog);
List<Map<String, Object>> moduleOperationStats = operLogService.getModuleOperationStats(operLog);
// 创建一个新的 Map 来组织数据
Map<String, Object> result = new LinkedHashMap<>();
result.put("successStats", successStats);
result.put("failureStats", failureStats);
result.put("statusStats", statusStats);
result.put("moduleOperationStats", moduleOperationStats);
result.put("total",
successStats.size() + failureStats.size() + statusStats.size() + moduleOperationStats.size());
return R.ok(result);
}
}

View File

@ -0,0 +1,72 @@
package com.boyue.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.constant.CacheConstants;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.CacheUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.system.domain.SysUserOnline;
import com.boyue.system.service.ISysUserOnlineService;
/**
* 在线用户监控
*
* @author boyue
*/
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController {
@Autowired
private ISysUserOnlineService userOnlineService;
@PreAuthorize("@ss.hasPermi('monitor:online:list')")
@GetMapping("/list")
public TableDataInfo list(String ipaddr, String userName) {
Collection<String> keys = CacheUtils.getkeys(CacheConstants.LOGIN_TOKEN_KEY);
List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
for (String key : keys) {
LoginUser user = CacheUtils.get(CacheConstants.LOGIN_TOKEN_KEY, key, LoginUser.class);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
} else if (StringUtils.isNotEmpty(ipaddr)) {
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
} else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) {
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
} else {
userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
}
}
Collections.reverse(userOnlineList);
userOnlineList.removeAll(Collections.singleton(null));
return getDataTable(userOnlineList);
}
/**
* 强退用户
*/
@PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/{tokenId}")
public AjaxResult forceLogout(@PathVariable String tokenId) {
CacheUtils.removeIfPresent(CacheConstants.LOGIN_TOKEN_KEY, tokenId);
return success();
}
}

View File

@ -0,0 +1,139 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Anonymous;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.system.domain.SysConfig;
import com.boyue.system.service.ISysConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 参数配置 信息操作处理
*
* @author boyue
*/
@Tag(name = "参数配置")
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController {
@Autowired
private ISysConfigService configService;
/**
* 获取参数配置列表
*/
@Operation(summary = "获取参数配置列表")
@PreAuthorize("@ss.hasPermi('system:config:list')")
@GetMapping("/list")
public TableDataInfo list(SysConfig config) {
startPage();
List<SysConfig> list = configService.selectConfigList(config);
return getDataTable(list);
}
@Operation(summary = "参数管理")
@Log(title = "参数管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:config:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysConfig config) {
List<SysConfig> list = configService.selectConfigList(config);
ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
util.exportExcel(response, list, "参数数据");
}
/**
* 根据参数编号获取详细信息
*/
@Operation(summary = "根据参数编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{configId}")
public AjaxResult getInfo(@PathVariable(name = "configId") Long configId) {
return success(configService.selectConfigById(configId));
}
/**
* 根据参数键名查询参数值
*/
@Operation(summary = "根据参数键名查询参数值")
@GetMapping(value = "/configKey/{configKey}")
@Anonymous
public AjaxResult getConfigKey(@PathVariable(name = "configKey") String configKey) {
return success(configService.selectConfigByKey(configKey));
}
/**
* 新增参数配置
*/
@Operation(summary = "新增参数配置")
@PreAuthorize("@ss.hasPermi('system:config:add')")
@Log(title = "参数管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config) {
if (!configService.checkConfigKeyUnique(config)) {
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setCreateBy(getUsername());
return toAjax(configService.insertConfig(config));
}
/**
* 修改参数配置
*/
@Operation(summary = "修改参数配置")
@PreAuthorize("@ss.hasPermi('system:config:edit')")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config) {
if (!configService.checkConfigKeyUnique(config)) {
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setUpdateBy(getUsername());
return toAjax(configService.updateConfig(config));
}
/**
* 删除参数配置
*/
@Operation(summary = "删除参数配置")
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}")
public AjaxResult remove(@PathVariable(name = "configIds") Long[] configIds) {
configService.deleteConfigByIds(configIds);
return success();
}
/**
* 刷新参数缓存
*/
@Operation(summary = "刷新参数缓存")
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache() {
configService.resetConfigCache();
return success();
}
}

View File

@ -0,0 +1,132 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.constant.UserConstants;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysDept;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.StringUtils;
import com.boyue.system.service.ISysDeptService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 部门信息
*
* @author boyue
*/
@Tag(name = "部门信息")
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController {
@Autowired
private ISysDeptService deptService;
/**
* 获取部门列表
*/
@Operation(summary = "获取部门列表")
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list")
public AjaxResult list(SysDept dept) {
List<SysDept> depts = deptService.selectDeptList(dept);
return success(depts);
}
/**
* 查询部门列表排除节点
*/
@Operation(summary = "查询部门列表", description = "排除节点")
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list/exclude/{deptId}")
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
List<SysDept> depts = deptService.selectDeptList(new SysDept());
depts.removeIf(d -> d.getDeptId().intValue() == deptId
|| ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
return success(depts);
}
/**
* 根据部门编号获取详细信息
*/
@Operation(summary = "根据部门编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:dept:query')")
@GetMapping(value = "/{deptId}")
public AjaxResult getInfo(@PathVariable(name = "deptId") Long deptId) {
deptService.checkDeptDataScope(deptId);
return success(deptService.selectDeptById(deptId));
}
/**
* 新增部门
*/
@Operation(summary = "新增部门")
@PreAuthorize("@ss.hasPermi('system:dept:add')")
@Log(title = "部门管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept) {
if (!deptService.checkDeptNameUnique(dept)) {
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
dept.setCreateBy(getUsername());
return toAjax(deptService.insertDept(dept));
}
/**
* 修改部门
*/
@Operation(summary = "修改部门")
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
@Log(title = "部门管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDept dept) {
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (!deptService.checkDeptNameUnique(dept)) {
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
} else if (dept.getParentId().equals(deptId)) {
return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
} else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
&& deptService.selectNormalChildrenDeptById(deptId) > 0) {
return error("该部门包含未停用的子部门!");
}
dept.setUpdateBy(getUsername());
return toAjax(deptService.updateDept(dept));
}
/**
* 删除部门
*/
@Operation(summary = "删除部门")
@PreAuthorize("@ss.hasPermi('system:dept:remove')")
@Log(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}")
public AjaxResult remove(@PathVariable(name = "deptId") Long deptId) {
if (deptService.hasChildByDeptId(deptId)) {
return warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId)) {
return warn("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
}

View File

@ -0,0 +1,126 @@
package com.boyue.web.controller.system;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysDictData;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.system.service.ISysDictDataService;
import com.boyue.system.service.ISysDictTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 数据字典信息
*
* @author boyue
*/
@Tag(name = "数据字典信息(数据)")
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController {
@Autowired
private ISysDictDataService dictDataService;
@Autowired
private ISysDictTypeService dictTypeService;
@Operation(summary = "查询字典数据列表")
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictData dictData) {
startPage();
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
return getDataTable(list);
}
@Operation(summary = "导出字典数据列表")
@Log(title = "字典数据", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictData dictData) {
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
util.exportExcel(response, list, "字典数据");
}
/**
* 查询字典数据详细
*/
@Operation(summary = "查询字典数据详细")
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictCode}")
public AjaxResult getInfo(@PathVariable(name = "dictCode") Long dictCode) {
return success(dictDataService.selectDictDataById(dictCode));
}
/**
* 根据字典类型查询字典数据信息
*/
@Operation(summary = "根据字典类型查询字典数据信息")
@GetMapping(value = "/type/{dictType}")
public AjaxResult dictType(@PathVariable(name = "dictType") String dictType) {
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (StringUtils.isNull(data)) {
data = new ArrayList<SysDictData>();
}
return success(data);
}
/**
* 新增字典类型
*/
@Operation(summary = "新增字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictData dict) {
dict.setCreateBy(getUsername());
return toAjax(dictDataService.insertDictData(dict));
}
/**
* 修改保存字典类型
*/
@Operation(summary = "修改保存字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictData dict) {
dict.setUpdateBy(getUsername());
return toAjax(dictDataService.updateDictData(dict));
}
/**
* 删除字典类型
*/
@Operation(summary = "删除字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public AjaxResult remove(@PathVariable(name = "dictCodes") Long[] dictCodes) {
dictDataService.deleteDictDataByIds(dictCodes);
return success();
}
}

View File

@ -0,0 +1,135 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysDictType;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.system.service.ISysDictTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 数据字典信息
*
* @author boyue
*/
@Tag(name = "数据字典信息(类型)")
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController {
@Autowired
private ISysDictTypeService dictTypeService;
@Operation(summary = "查询字典类型列表")
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictType dictType) {
startPage();
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
return getDataTable(list);
}
@Operation(summary = "导出字典类型列表")
@Log(title = "字典类型", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictType dictType) {
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
util.exportExcel(response, list, "字典类型");
}
/**
* 查询字典类型详细
*/
@Operation(summary = "查询字典类型详细")
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictId}")
public AjaxResult getInfo(@PathVariable(name = "dictId") Long dictId) {
return success(dictTypeService.selectDictTypeById(dictId));
}
/**
* 新增字典类型
*/
@Operation(summary = "新增字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典类型", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict) {
if (!dictTypeService.checkDictTypeUnique(dict)) {
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setCreateBy(getUsername());
return toAjax(dictTypeService.insertDictType(dict));
}
/**
* 修改字典类型
*/
@Operation(summary = "修改字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict) {
if (!dictTypeService.checkDictTypeUnique(dict)) {
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setUpdateBy(getUsername());
return toAjax(dictTypeService.updateDictType(dict));
}
/**
* 删除字典类型
*/
@Operation(summary = "删除字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictIds}")
public AjaxResult remove(@PathVariable(name = "dictIds") Long[] dictIds) {
dictTypeService.deleteDictTypeByIds(dictIds);
return success();
}
/**
* 刷新字典缓存
*/
@Operation(summary = "刷新字典缓存")
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache() {
dictTypeService.resetDictCache();
return success();
}
/**
* 获取字典选择框列表
*/
@Operation(summary = "获取字典选择框列表")
@GetMapping("/optionselect")
public AjaxResult optionselect() {
List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
return success(dictTypes);
}
}

View File

@ -0,0 +1,34 @@
package com.boyue.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.config.BoYueConfig;
import com.boyue.common.utils.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 首页
*
* @author boyue
*/
@Tag(name = "首页")
@RestController
public class SysIndexController {
/** 系统基础配置 */
@Autowired
private BoYueConfig BoYueConfig;
/**
* 访问首页提示语
*/
@Operation(summary = "访问首页", description = "提示语")
@RequestMapping("/")
public String index() {
return StringUtils.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。", BoYueConfig.getName(), BoYueConfig.getVersion());
}
}

View File

@ -0,0 +1,112 @@
package com.boyue.web.controller.system;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.constant.Constants;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysMenu;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginBody;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.utils.SecurityUtils;
import com.boyue.file.utils.FileOperateUtils;
import com.boyue.framework.web.service.SysLoginService;
import com.boyue.framework.web.service.SysPermissionService;
import com.boyue.framework.web.service.TokenService;
import com.boyue.system.service.ISysMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 登录验证
*
* @author boyue
*/
@Tag(name = "登录验证")
@RestController
public class SysLoginController {
@Autowired
private SysLoginService loginService;
@Autowired
private ISysMenuService menuService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@Operation(summary = "登录方法")
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(
loginBody.getUsername(),
loginBody.getPassword(),
loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
/**
* 获取用户信息
*
* @return 用户信息
*/
@Operation(summary = "获取用户信息")
@GetMapping("getInfo")
public AjaxResult getInfo() {
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions)) {
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
if (user.getAvatar() != null) {
try {
user.setAvatar(FileOperateUtils.getURL(user.getAvatar()));
} catch (Exception e) {
}
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
return ajax;
}
/**
* 获取路由信息
*
* @return 路由信息
*/
@Operation(summary = "获取路由信息")
@GetMapping("getRouters")
public AjaxResult getRouters() {
Long userId = SecurityUtils.getUserId();
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
}

View File

@ -0,0 +1,138 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.constant.UserConstants;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysMenu;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.StringUtils;
import com.boyue.system.service.ISysMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 菜单信息
*
* @author boyue
*/
@Tag(name = "菜单信息")
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
@Autowired
private ISysMenuService menuService;
/**
* 获取菜单列表
*/
@Operation(summary = "获取菜单列表")
@PreAuthorize("@ss.hasPermi('system:menu:list')")
@GetMapping("/list")
public AjaxResult list(SysMenu menu) {
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menus);
}
/**
* 根据菜单编号获取详细信息
*/
@Operation(summary = "根据菜单编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:menu:query')")
@GetMapping(value = "/{menuId}")
public AjaxResult getInfo(@PathVariable(name = "menuId") Long menuId) {
return success(menuService.selectMenuById(menuId));
}
/**
* 获取菜单下拉树列表
*/
@Operation(summary = "获取菜单下拉树列表")
@GetMapping("/treeselect")
public AjaxResult treeselect(SysMenu menu) {
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menuService.buildMenuTreeSelect(menus));
}
/**
* 加载对应角色菜单列表树
*/
@Operation(summary = "加载对应角色菜单列表树")
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
List<SysMenu> menus = menuService.selectMenuList(getUserId());
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
ajax.put("menus", menuService.buildMenuTreeSelect(menus));
return ajax;
}
/**
* 新增菜单
*/
@Operation(summary = "新增菜单")
@PreAuthorize("@ss.hasPermi('system:menu:add')")
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu) {
if (!menuService.checkMenuNameUnique(menu)) {
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
return error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
menu.setCreateBy(getUsername());
return toAjax(menuService.insertMenu(menu));
}
/**
* 修改菜单
*/
@Operation(summary = "修改菜单")
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu) {
if (!menuService.checkMenuNameUnique(menu)) {
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
return error("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
} else if (menu.getMenuId().equals(menu.getParentId())) {
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
menu.setUpdateBy(getUsername());
return toAjax(menuService.updateMenu(menu));
}
/**
* 删除菜单
*/
@Operation(summary = "删除菜单")
@PreAuthorize("@ss.hasPermi('system:menu:remove')")
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{menuId}")
public AjaxResult remove(@PathVariable("menuId") Long menuId) {
if (menuService.hasChildByMenuId(menuId)) {
return warn("存在子菜单,不允许删除");
}
if (menuService.checkMenuExistRole(menuId)) {
return warn("菜单已分配,不允许删除");
}
return toAjax(menuService.deleteMenuById(menuId));
}
}

View File

@ -0,0 +1,97 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.system.domain.SysNotice;
import com.boyue.system.service.ISysNoticeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 公告 信息操作处理
*
* @author boyue
*/
@Tag(name = "公告", description = "信息操作处理")
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController {
@Autowired
private ISysNoticeService noticeService;
/**
* 获取通知公告列表
*/
@Operation(summary = "获取通知公告列表")
@PreAuthorize("@ss.hasPermi('system:notice:list')")
@GetMapping("/list")
public TableDataInfo list(SysNotice notice) {
startPage();
List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list);
}
/**
* 根据通知公告编号获取详细信息
*/
@Operation(summary = "根据通知公告编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:notice:query')")
@GetMapping(value = "/{noticeId}")
public AjaxResult getInfo(@PathVariable(name = "noticeId") Long noticeId) {
return success(noticeService.selectNoticeById(noticeId));
}
/**
* 新增通知公告
*/
@Operation(summary = "新增通知公告")
@PreAuthorize("@ss.hasPermi('system:notice:add')")
@Log(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysNotice notice) {
notice.setCreateBy(getUsername());
return toAjax(noticeService.insertNotice(notice));
}
/**
* 修改通知公告
*/
@Operation(summary = "修改通知公告")
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
@Log(title = "通知公告", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysNotice notice) {
notice.setUpdateBy(getUsername());
return toAjax(noticeService.updateNotice(notice));
}
/**
* 删除通知公告
*/
@Operation(summary = "删除通知公告")
@PreAuthorize("@ss.hasPermi('system:notice:remove')")
@Log(title = "通知公告", businessType = BusinessType.DELETE)
@DeleteMapping("/{noticeIds}")
public AjaxResult remove(@PathVariable(name = "noticeIds") Long[] noticeIds) {
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
}
}

View File

@ -0,0 +1,129 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.system.domain.SysPost;
import com.boyue.system.service.ISysPostService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 岗位信息操作处理
*
* @author boyue
*/
@Tag(name = "岗位信息操作处理")
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController {
@Autowired
private ISysPostService postService;
/**
* 获取岗位列表
*/
@Operation(summary = "获取岗位列表")
@PreAuthorize("@ss.hasPermi('system:post:list')")
@GetMapping("/list")
public TableDataInfo list(SysPost post) {
startPage();
List<SysPost> list = postService.selectPostList(post);
return getDataTable(list);
}
@Operation(summary = "导出岗位列表")
@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysPost post) {
List<SysPost> list = postService.selectPostList(post);
ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
util.exportExcel(response, list, "岗位数据");
}
/**
* 根据岗位编号获取详细信息
*/
@Operation(summary = "根据岗位编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:post:query')")
@GetMapping(value = "/{postId}")
public AjaxResult getInfo(@PathVariable(name = "postId") Long postId) {
return success(postService.selectPostById(postId));
}
/**
* 新增岗位
*/
@Operation(summary = "新增岗位")
@PreAuthorize("@ss.hasPermi('system:post:add')")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post) {
if (!postService.checkPostNameUnique(post)) {
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) {
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setCreateBy(getUsername());
return toAjax(postService.insertPost(post));
}
/**
* 修改岗位
*/
@Operation(summary = "修改岗位")
@PreAuthorize("@ss.hasPermi('system:post:edit')")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post) {
if (!postService.checkPostNameUnique(post)) {
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) {
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setUpdateBy(getUsername());
return toAjax(postService.updatePost(post));
}
/**
* 删除岗位
*/
@Operation(summary = "删除岗位")
@PreAuthorize("@ss.hasPermi('system:post:remove')")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public AjaxResult remove(@PathVariable(name = "postIds") Long[] postIds) {
return toAjax(postService.deletePostByIds(postIds));
}
/**
* 获取岗位选择框列表
*/
@Operation(summary = "获取岗位选择框列表")
@GetMapping("/optionselect")
public AjaxResult optionselect() {
List<SysPost> posts = postService.selectPostAll();
return success(posts);
}
}

View File

@ -0,0 +1,149 @@
package com.boyue.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.DateUtils;
import com.boyue.common.utils.SecurityUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.file.FileUtils;
import com.boyue.common.utils.file.MimeTypeUtils;
import com.boyue.file.utils.FileOperateUtils;
import com.boyue.framework.web.service.TokenService;
import com.boyue.system.service.ISysUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 个人信息 业务处理
*
* @author boyue
*/
@Tag(name = "个人信息", description = "业务处理")
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController {
@Autowired
private ISysUserService userService;
@Autowired
private TokenService tokenService;
/**
* 个人信息
*/
@Operation(summary = "个人信息")
@GetMapping
public AjaxResult profile() {
LoginUser loginUser = getLoginUser();
SysUser user = loginUser.getUser();
if (user.getAvatar() != null) {
try {
user.setAvatar(FileOperateUtils.getURL(user.getAvatar()));
} catch (Exception e) {
}
}
AjaxResult ajax = AjaxResult.success(user);
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
return ajax;
}
/**
* 修改用户
*/
@Operation(summary = "修改用户")
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult updateProfile(@RequestBody SysUser user) {
LoginUser loginUser = getLoginUser();
SysUser sysUser = loginUser.getUser();
user.setUserName(sysUser.getUserName());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUserId(sysUser.getUserId());
user.setPassword(null);
user.setAvatar(null);
user.setDeptId(null);
if (userService.updateUserProfile(user) > 0) {
// 更新缓存用户信息
sysUser.setNickName(user.getNickName());
sysUser.setPhonenumber(user.getPhonenumber());
sysUser.setEmail(user.getEmail());
sysUser.setSex(user.getSex());
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改个人信息异常,请联系管理员");
}
/**
* 重置密码
*/
@Operation(summary = "重置密码")
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword) {
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password)) {
return error("修改密码失败,旧密码错误");
}
if (SecurityUtils.matchesPassword(newPassword, password)) {
return error("新密码不能与旧密码相同");
}
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0) {
// 更新缓存用户密码
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改密码异常,请联系管理员");
}
/**
* 头像上传
*/
@Operation(summary = "头像上传")
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception {
if (!file.isEmpty()) {
LoginUser loginUser = getLoginUser();
String extractPath = loginUser.getUsername() + "/" + loginUser.getUserId() + "";
String fileName = DateUtils.dateTimeNow() + "-avatar." + FileUtils.getExtension(file);
String filePath = "avatar/" + extractPath + "/" + fileName;
String url = FileOperateUtils.upload(filePath, file, MimeTypeUtils.IMAGE_EXTENSION);
if (userService.updateUserAvatar(loginUser.getUsername(), filePath)) {
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", url);
// 更新缓存用户头像
loginUser.getUser().setAvatar(filePath);
tokenService.setLoginUser(loginUser);
return ajax;
}
}
return error("上传图片异常,请联系管理员");
}
}

View File

@ -0,0 +1,42 @@
package com.boyue.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.model.RegisterBody;
import com.boyue.common.utils.StringUtils;
import com.boyue.framework.web.service.SysRegisterService;
import com.boyue.system.service.ISysConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 注册验证
*
* @author boyue
*/
@Tag(name = "注册验证")
@RestController
public class SysRegisterController extends BaseController {
@Autowired
private SysRegisterService registerService;
@Autowired
private ISysConfigService configService;
@Operation(summary = "注册方法")
@PostMapping("/register")
public AjaxResult register(@RequestBody RegisterBody user) {
if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) {
return error("当前系统没有开启注册功能!");
}
String msg = registerService.register(user);
return StringUtils.isEmpty(msg) ? success() : error(msg);
}
}

View File

@ -0,0 +1,260 @@
package com.boyue.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysDept;
import com.boyue.common.core.domain.entity.SysRole;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.framework.web.service.SysPermissionService;
import com.boyue.framework.web.service.TokenService;
import com.boyue.system.domain.SysUserRole;
import com.boyue.system.service.ISysDeptService;
import com.boyue.system.service.ISysRoleService;
import com.boyue.system.service.ISysUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 角色信息
*
* @author boyue
*/
@Tag(name = "角色信息")
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController {
@Autowired
private ISysRoleService roleService;
@Autowired
private TokenService tokenService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private ISysUserService userService;
@Autowired
private ISysDeptService deptService;
@Operation(summary = "获取角色列表")
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/list")
public TableDataInfo list(SysRole role) {
startPage();
List<SysRole> list = roleService.selectRoleList(role);
return getDataTable(list);
}
@Operation(summary = "导出角色列表")
@Log(title = "角色管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:role:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysRole role) {
List<SysRole> list = roleService.selectRoleList(role);
ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
util.exportExcel(response, list, "角色数据");
}
/**
* 根据角色编号获取详细信息
*/
@Operation(summary = "根据角色编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/{roleId}")
public AjaxResult getInfo(@PathVariable(name = "roleId") Long roleId) {
roleService.checkRoleDataScope(roleId);
return success(roleService.selectRoleById(roleId));
}
/**
* 新增角色
*/
@Operation(summary = "新增角色")
@PreAuthorize("@ss.hasPermi('system:role:add')")
@Log(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role) {
if (!roleService.checkRoleNameUnique(role)) {
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
} else if (!roleService.checkRoleKeyUnique(role)) {
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setCreateBy(getUsername());
return toAjax(roleService.insertRole(role));
}
/**
* 修改保存角色
*/
@Operation(summary = "修改保存角色")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (!roleService.checkRoleNameUnique(role)) {
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
} else if (!roleService.checkRoleKeyUnique(role)) {
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
if (roleService.updateRole(role) > 0) {
// 更新缓存用户权限
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) {
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();
}
return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
}
/**
* 修改保存数据权限
*/
@Operation(summary = "修改保存数据权限")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/dataScope")
public AjaxResult dataScope(@RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
/**
* 状态修改
*/
@Operation(summary = "状态修改")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
role.setUpdateBy(getUsername());
return toAjax(roleService.updateRoleStatus(role));
}
/**
* 删除角色
*/
@Operation(summary = "删除角色")
@PreAuthorize("@ss.hasPermi('system:role:remove')")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}")
public AjaxResult remove(@PathVariable(name = "roleIds") Long[] roleIds) {
return toAjax(roleService.deleteRoleByIds(roleIds));
}
/**
* 获取角色选择框列表
*/
@Operation(summary = "获取角色选择框列表")
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping("/optionselect")
public AjaxResult optionselect() {
return success(roleService.selectRoleAll());
}
/**
* 查询已分配用户角色列表
*/
@Operation(summary = "查询已分配用户角色列表")
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/allocatedList")
public TableDataInfo allocatedList(SysUser user) {
startPage();
List<SysUser> list = userService.selectAllocatedList(user);
return getDataTable(list);
}
/**
* 查询未分配用户角色列表
*/
@Operation(summary = "查询未分配用户角色列表")
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo unallocatedList(SysUser user) {
startPage();
List<SysUser> list = userService.selectUnallocatedList(user);
return getDataTable(list);
}
/**
* 取消授权用户
*/
@Operation(summary = "取消授权用户")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancel")
public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) {
return toAjax(roleService.deleteAuthUser(userRole));
}
/**
* 批量取消授权用户
*/
@Operation(summary = "批量取消授权用户")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancelAll")
public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) {
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
* 批量选择用户授权
*/
@Operation(summary = "批量选择用户授权")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/selectAll")
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) {
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
* 获取对应角色部门树列表
*/
@Operation(summary = "获取对应角色部门树列表")
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/deptTree/{roleId}")
public AjaxResult deptTree(@PathVariable("roleId") Long roleId) {
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
return ajax;
}
}

View File

@ -0,0 +1,247 @@
package com.boyue.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysDept;
import com.boyue.common.core.domain.entity.SysRole;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.SecurityUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.poi.ExcelUtil;
import com.boyue.system.service.ISysDeptService;
import com.boyue.system.service.ISysPostService;
import com.boyue.system.service.ISysRoleService;
import com.boyue.system.service.ISysUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 用户信息
*
* @author boyue
*/
@Tag(name = "用户信息")
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
@Autowired
private ISysUserService userService;
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysDeptService deptService;
@Autowired
private ISysPostService postService;
/**
* 获取用户列表
*/
@Operation(summary = "获取用户列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage();
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list);
}
@Operation(summary = "导出用户列表")
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysUser user) {
List<SysUser> list = userService.selectUserList(user);
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.exportExcel(response, list, "用户数据");
}
@Operation(summary = "导入用户列表")
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('system:user:import')")
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
List<SysUser> userList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = userService.importUser(userList, updateSupport, operName);
return success(message);
}
@Operation(summary = "获取导入用户模板")
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.importTemplateExcel(response, "用户数据");
}
/**
* 根据用户编号获取详细信息
*/
@Operation(summary = "根据用户编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
AjaxResult ajax = AjaxResult.success();
if (StringUtils.isNotNull(userId)) {
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles
: roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
/**
* 新增用户
*/
@Operation(summary = "新增用户")
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) {
if (!userService.checkUserNameUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
return toAjax(userService.insertUser(user));
}
/**
* 修改用户
*/
@Operation(summary = "修改用户")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (!userService.checkUserNameUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUpdateBy(getUsername());
return toAjax(userService.updateUser(user));
}
/**
* 删除用户
*/
@Operation(summary = "删除用户")
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable(name = "userIds") Long[] userIds) {
if (ArrayUtils.contains(userIds, getUserId())) {
return error("当前用户不能删除");
}
return toAjax(userService.deleteUserByIds(userIds));
}
/**
* 重置密码
*/
@Operation(summary = "重置密码")
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
public AjaxResult resetPwd(@RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
user.setUpdateBy(getUsername());
return toAjax(userService.resetPwd(user));
}
/**
* 状态修改
*/
@Operation(summary = "状态修改")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setUpdateBy(getUsername());
return toAjax(userService.updateUserStatus(user));
}
/**
* 根据用户编号获取授权角色
*/
@Operation(summary = "根据用户编号获取授权角色")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId) {
AjaxResult ajax = AjaxResult.success();
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
ajax.put("user", user);
ajax.put("roles", SysUser.isAdmin(userId) ? roles
: roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
return ajax;
}
/**
* 用户授权角色
*/
@Operation(summary = "用户授权角色")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
userService.checkUserDataScope(userId);
userService.insertUserAuth(userId, roleIds);
return success();
}
/**
* 获取部门树列表
*/
@Operation(summary = "获取部门树列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept) {
return success(deptService.selectDeptTreeList(dept));
}
}

View File

@ -0,0 +1,104 @@
package com.boyue.web.core.config;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.boyue.common.constant.HttpStatus;
import com.boyue.common.core.domain.AjaxResult;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
/**
* 验证码操作处理
*
* @author Dftre
*/
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI springShopOpenApi() {
Schema<?> codeSchema = new IntegerSchema().example(HttpStatus.SUCCESS); // 示例状态码
Schema<?> msgSchema = new StringSchema().example("操作成功"); // 示例消息
ObjectSchema dataSchema = new ObjectSchema(); // 数据可以是任意类型这里简单定义为ObjectSchema
// 定义AjaxResult的Schema
ObjectSchema ajaxResultSchema = new ObjectSchema();
ajaxResultSchema.addProperty(AjaxResult.CODE_TAG, codeSchema);
ajaxResultSchema.addProperty(AjaxResult.MSG_TAG, msgSchema);
ajaxResultSchema.addProperty(AjaxResult.DATA_TAG, dataSchema);
Components components = new Components();
components.addSchemas("AjaxResult", ajaxResultSchema);
return new OpenAPI()
.components(components)
.info(new Info().title("boyue 二开接口")
.description("boyue 二开接口 API文档")
.version("v1")
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("外部文档")
.url("/doc.html"));
}
@Bean
public GroupedOpenApi sysApi() {
return GroupedOpenApi.builder()
.group("sys系统模块")
.packagesToScan("com.boyue.web.controller.system")
.build();
}
@Bean
public GroupedOpenApi commonApi() {
return GroupedOpenApi.builder()
.group("基础模块")
.packagesToScan("com.boyue.web.controller.common")
.build();
}
@Bean
public GroupedOpenApi payApi() {
return GroupedOpenApi.builder()
.group("支付模块")
.pathsToMatch("/pay/**")
.build();
}
@Bean
public GroupedOpenApi fileApi() {
return GroupedOpenApi.builder()
.group("文件模块")
.packagesToScan("com.boyue.file.controller")
.build();
}
@Bean
public GroupedOpenApi authApi() {
return GroupedOpenApi.builder()
.group("认证模块")
.packagesToScan("com.boyue.auth.controller")
.build();
}
/**
* 确保扫描到模块化后的司法局模块
*/
@Bean
public GroupedOpenApi hasfjModelsApi() {
return GroupedOpenApi.builder()
.group("淮安市司法局模块(models)")
.packagesToScan("com.boyue.hasfj.controller")
.pathsToMatch("/hasfj/**")
.build();
}
}

View File

@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar

View File

@ -0,0 +1,37 @@
# 请输入自己的appid和appsecret
oauth:
wx:
miniapp:
appId: appId
appSecret: appSecret
url: https://api.weixin.qq.com/sns/jscode2session
pub:
appId: appId
appSecret: appSecret
url: https://api.weixin.qq.com/sns/oauth2/access_token
tfa:
phone:
dysms:
# 阿里云 AccessKey ID
accessKeyId: appId
# 阿里云 AccessKey Secret
accessKeySecret: appSecret
# 短信模板
template:
VerificationCode:
# 短信模板编码
templateCode: SMS_123456789
# 短信签名
signName: 阿里云短信测试
# 短信模板必需的数据名称多个key以逗号分隔此处配置作为校验
keys: code
spring:
mail:
host: smtp.qq.com
# 邮箱地址
username: email
# 授权码
password: password

View File

@ -0,0 +1,91 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
dynamic:
primary: MASTER
xa: true
datasource:
# # 主库数据源开发
# MASTER:
# url: jdbc:mysql://127.0.0.1/boyuehasfj?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: 123456
# 主库数据源开发
MASTER:
url: jdbc:mysql://222.184.49.22:3307/boyuehasfj?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: boyue1!Z
# MASTER:
# url: jdbc:mysql://127.0.0.1/boyuekfcode
# username: root
# password: 123456
# 从库数据源
# SLAVE:
# url: jdbc:mysql://127.0.0.1/boyue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: 123456
druid:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 配置监控统计拦截的filters stat=>监控统计 wall=>防SQL注入 slf4j log4j2=>日志记录
filters: stat,wall,slf4j,log4j2
# 通过connectProperties属性来打开mergeSql功能慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 配置spring监控的匹配类
aop-patterns: com.boyue.*.service.*,com.boyue.*.mapper.*
# Web应用 和 URL监控 配置
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
session-stat-enable: false # 是否开启session统计功能
# 监控视图配置
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: true # 是否可以重置日志
login-username: boyue # 用户名
login-password: boyue123 # 密码
allow: "" # IP白名单 (没有配置或者为空,则允许所有访问)
deny: "" # IP黑名单 (存在共同时deny优先于allow)
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# 是否开启分布式事务如不开启请删除atomikos插件否则atomikos相关驱动虽不生效但仍会启动
atomikos:
enabled: false

View File

@ -0,0 +1,53 @@
# local配置
local:
enable: true
primary: MASTER
client:
MASTER:
permission: public
path: /data/files/master
# 建议public权限的api以 /profile开头就不需要再从SecurityConfig配置权限了
api: /profile/files/master
# SLAVE:
# permission: private
# path: /data/files/slave
# private需要自己编写一个api例如FileController中的/file/resource
# api: /file/resource
# Minio配置
minio:
enable: false
primary: MASTER
client:
MASTER:
permission: public
url: http://localhost:9000
accessKey:
secretKey:
bucketName: boyue
# SLAVE:
# permission: private
# url: http://localhost:9000
# accessKey: minioadmin
# secretKey: minioadmin
# bucketName: ry
# oss配置
oss:
enable: false
primary: MASTER
client:
MASTER:
permission: public
accessKeyId: accessKeyId
accessKeySecret: accessKeySecret
bucketName: boyue
endpoint: oss-cn-beijing.aliyuncs.com
# SLAVE:
# permission: private
# accessKeyId:
# accessKeySecret:
# bucketName:
# endpoint:

View File

@ -0,0 +1,7 @@
netty:
websocket:
maxMessageSize: 65536
bossThreads: 4
workerThreads: 16
port: 8081
enable: true

View File

@ -0,0 +1,39 @@
spring:
data:
# redis 配置
redis:
# 地址 开发环境
# host: localhost
# 地址 生产环境
host: 222.184.49.22
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码 开发环境
# password: 123456
# 密码 生产环境
password: boyue123
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
#配置rabbitMq 服务器
rabbitmq:
enable: false
host: 127.0.0.1
port: 5672
username: guest
password: guest

View File

@ -0,0 +1,27 @@
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
createSqlSessionFactory:
# 选择MyBatis配置方式mybatis / mybatis-plus
use: mybatis-plus
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.boyue.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# MyBatis Plus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.boyue.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml

View File

@ -0,0 +1,30 @@
# 当前支付模块写的并不完善,请根据自己的业务需求进行修改
# 回调地址使用的内网穿透http://e2vca6.natappfree.cc
pay:
# https://doc.shouqianba.com/zh-cn/
sqb:
enabled: false
appId: "appId"
apiDomain: "apiDomain"
terminalSn: "terminalSn"
terminalKey: "terminalKey"
vendorSn: "vendorSn"
vendorKey: "vendorKey"
publicKey: classpath:pay/sqb/sqb_public_key.pem
notifyUrl: http://e2vca6.natappfree.cc/pay/sqb/notify
# https://opendocs.alipay.com/open/02np95
alipay:
enabled: false
appId: appid
appPrivateKey: classpath:pay/alipay/alipay_private_key.pem
alipayPublicKey: classpath:pay/alipay/alipay_public_key.pem
notifyUrl: http://e2vca6.natappfree.cc/alipay/notify
# https://github.com/wechatpay-apiv3/wechatpay-java
wechat:
enabled: false
appId: appid
apiV3Key: apiV3Key
privateKeyPath: classpath:pay/wx/apiclient_key.pem
merchantId: merchantId
merchantSerialNumber: merchantSerialNumber
notifyUrl: http://e2vca6.natappfree.cc/pay/wechat/notify

View File

@ -0,0 +1,121 @@
# 项目相关配置
boyue:
# 名称
name: boyue
# 版本
version: 3.8.5
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/boyue/uploadPathLinux配置 /home/boyue/uploadPath
# profile: D:/boyue/hasfj/uploadPath
profile: /home/boyue/hasfj/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数组计算 char 字符验证
captchaType: math
# 指定默认文件服务类型(值为local代表使用本地作为文件操作服务,minio代表使用minio作为文件操作服务,oss代表使用oss作为文件操作服务)
fileServer: local
# 指定默认文件上传方法最大文件大小(MB)
fileMaxSize: 500
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 9799
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
"[com.boyue]": DEBUG
"[org]": WARN
"[org.springframework]": WARN
"[org.apache]": WARN
"[org.springframework.context.support.PostProcessorRegistrationDelegate]": ERROR
"[com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration]": ERROR
"[com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties]": ERROR
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
ip:
maxRetryCount: 15
lockTime: 15
# Spring配置
spring:
#给项目来个名字
application:
name: boyue
cache:
# 指定缓存类型 jcache 本地缓存 redis 缓存
type: redis
redis:
# 指定存活时间ms
time-to-live: 86400000
# 指定前缀
use-key-prefix: true
# 是否缓存空值,可以防止缓存穿透
cache-null-values: true
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid,mybatis,auth,pay,middleware,lp,file
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 100MB
# 设置总上传的文件大小
max-request-size: 200MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*

View File

@ -0,0 +1,24 @@
Application Version: ${boyue.version}
Spring Boot Version: ${spring-boot.version}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,38 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/boyue/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.boyue" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- <plugins>
<plugin interceptor="com.boyue.mybatisinterceptor.interceptor.mybatis.MybatisInterceptor"></plugin>
</plugins> -->
</configuration>

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title th:text="${page.title}">典型案例</title>
<meta name="keywords" th:content="${page.keywords}" content="典型案例,淮安市司法局">
<meta name="description" th:content="${page.description}" content="淮安市司法局典型案例详情页">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.case-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 5px;
}
.case-title {
text-align: center;
padding: 20px 0;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.case-meta {
color: #666;
font-size: 14px;
margin-bottom: 20px;
text-align: center;
}
.case-content {
line-height: 1.8;
font-size: 16px;
}
.case-summary {
background-color: #f9f9f9;
padding: 15px;
border-left: 4px solid #28a745;
margin-bottom: 20px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<div class="case-container">
<div class="case-title">
<h2 th:text="${page.title}">典型案例标题</h2>
</div>
<div class="case-meta">
<span>发布时间:<span th:text="${#dates.format(page.createTime, 'yyyy-MM-dd')}">2025-05-28</span></span>
<span class="mx-3">|</span>
<span>作者:<span th:text="${page.author}">管理员</span></span>
<span class="mx-3">|</span>
<span>浏览次数:<span th:text="${page.viewCount}">0</span></span>
</div>
<div class="case-summary">
案例摘要:<span th:text="${page.description}">这是一个典型案例的摘要描述...</span>
</div>
<div class="case-content" th:utext="${page.content}">
<!-- 这里将显示HTML内容 -->
<p>典型案例内容将在这里显示...</p>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>典型案例列表 - 淮安市司法局</title>
<meta name="keywords" content="典型案例,淮安市司法局">
<meta name="description" content="淮安市司法局典型案例列表">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.page-header {
text-align: center;
margin-bottom: 30px;
}
.page-section {
margin-bottom: 40px;
}
.page-section h3 {
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
margin-bottom: 20px;
}
.page-list {
list-style: none;
padding: 0;
}
.page-list li {
padding: 12px 0;
border-bottom: 1px dashed #eee;
}
.page-list li .date {
float: right;
color: #999;
}
.page-list li a {
color: #333;
text-decoration: none;
}
.page-list li a:hover {
color: #007bff;
}
</style>
</head>
<body>
<div class="container">
<div class="page-container">
<div class="page-header">
<h2>典型案例</h2>
</div>
<div class="page-section">
<ul class="page-list">
<th:block th:if="${not #lists.isEmpty(caseList)}">
<li th:each="case : ${caseList}">
<span class="date" th:text="${#dates.format(case.createTime, 'yyyy-MM-dd')}">2025-05-28</span>
<a th:href="@{'/showcase.html?Id=' + ${case.formatId}}" th:text="${case.title}">典型案例标题</a>
</li>
</th:block>
<th:block th:if="${#lists.isEmpty(caseList)}">
<li>暂无相关典型案例</li>
</th:block>
</ul>
</div>
<div class="text-center mt-4">
<a href="/hasfj" class="btn btn-primary">返回首页</a>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title th:text="${page.title}">表单下载</title>
<meta name="keywords" th:content="${page.keywords}" content="表单下载,淮安市司法局">
<meta name="description" th:content="${page.description}" content="淮安市司法局表单下载页">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.form-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 5px;
}
.form-title {
text-align: center;
padding: 20px 0;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.form-meta {
color: #666;
font-size: 14px;
margin-bottom: 20px;
text-align: center;
}
.form-content {
line-height: 1.8;
font-size: 16px;
}
.download-btn {
text-align: center;
margin: 30px 0;
}
.download-btn .btn {
padding: 10px 30px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="container">
<div class="form-container">
<div class="form-title">
<h2 th:text="${page.title}">表单下载标题</h2>
</div>
<div class="form-meta">
<span>发布时间:<span th:text="${#dates.format(page.createTime, 'yyyy-MM-dd')}">2025-05-28</span></span>
<span class="mx-3">|</span>
<span>作者:<span th:text="${page.author}">管理员</span></span>
<span class="mx-3">|</span>
<span>浏览次数:<span th:text="${page.viewCount}">0</span></span>
</div>
<div class="form-content" th:utext="${page.content}">
<!-- 这里将显示HTML内容 -->
<p>表单说明内容将在这里显示...</p>
</div>
<div class="download-btn">
<a href="#" class="btn btn-primary" target="_blank">
<i class="fa fa-download"></i> 下载表单
</a>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表单下载列表 - 淮安市司法局</title>
<meta name="keywords" content="表单下载,淮安市司法局">
<meta name="description" content="淮安市司法局表单下载列表">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.page-header {
text-align: center;
margin-bottom: 30px;
}
.page-section {
margin-bottom: 40px;
}
.page-section h3 {
border-bottom: 2px solid #dc3545;
padding-bottom: 10px;
margin-bottom: 20px;
}
.page-list {
list-style: none;
padding: 0;
}
.page-list li {
padding: 12px 0;
border-bottom: 1px dashed #eee;
}
.page-list li .date {
float: right;
color: #999;
}
.page-list li a {
color: #333;
text-decoration: none;
}
.page-list li a:hover {
color: #dc3545;
}
.download-icon {
margin-right: 5px;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<div class="page-container">
<div class="page-header">
<h2>表单下载</h2>
</div>
<div class="page-section">
<ul class="page-list">
<th:block th:if="${not #lists.isEmpty(formList)}">
<li th:each="form : ${formList}">
<span class="date" th:text="${#dates.format(form.createTime, 'yyyy-MM-dd')}">2025-05-28</span>
<a th:href="@{'/table.html?Id=' + ${form.formatId}}" th:text="${form.title}">
<i class="download-icon">📄</i>表单标题
</a>
</li>
</th:block>
<th:block th:if="${#lists.isEmpty(formList)}">
<li>暂无相关表单</li>
</th:block>
</ul>
</div>
<div class="text-center mt-4">
<a href="/hasfj" class="btn btn-primary">返回首页</a>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,225 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>淮安市司法局</title>
<meta name="keywords" content="淮安市司法局,法律规定,典型案例,表单下载">
<meta name="description" content="淮安市司法局官方网站">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.page-header {
text-align: center;
margin-bottom: 30px;
}
.page-section {
margin-bottom: 40px;
}
.page-section h3 {
border-bottom: 2px solid #28a745;
padding-bottom: 10px;
margin-bottom: 20px;
}
.page-list {
list-style: none;
padding: 0;
}
.page-list li {
padding: 12px 0;
border-bottom: 1px dashed #eee;
}
.page-list li .date {
float: right;
color: #999;
}
.page-list li a {
color: #333;
text-decoration: none;
}
.page-list li a:hover {
color: #28a745;
}
.tab-content {
padding: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<div class="page-container">
<div class="page-header">
<h2>淮安市司法局</h2>
</div>
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="law-tab" data-bs-toggle="tab" data-bs-target="#law" type="button" role="tab" aria-controls="law" aria-selected="true">法律规定</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="case-tab" data-bs-toggle="tab" data-bs-target="#case" type="button" role="tab" aria-controls="case" aria-selected="false">典型案例</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="form-tab" data-bs-toggle="tab" data-bs-target="#form" type="button" role="tab" aria-controls="form" aria-selected="false">表单下载</button>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<!-- 法律规定 -->
<div class="tab-pane fade show active" id="law" role="tabpanel" aria-labelledby="law-tab">
<div class="page-section">
<ul class="page-list" id="lawList">
<li>
<span class="date">2025-05-28</span>
<a href="/show.html?Id=000001">劳动法第四十条解读</a>
</li>
<li>
<span class="date">2025-05-28</span>
<a href="/show.html?Id=000002">劳动法第四十一条解读</a>
</li>
<li>
<span class="date">2025-05-28</span>
<a href="/show.html?Id=000003">劳动法第四十二条解读</a>
</li>
</ul>
</div>
</div>
<!-- 典型案例 -->
<div class="tab-pane fade" id="case" role="tabpanel" aria-labelledby="case-tab">
<div class="page-section">
<ul class="page-list" id="caseList">
<li>
<span class="date">2025-05-28</span>
<a href="/showcase.html?Id=000001">某公司劳动纠纷案例一</a>
</li>
<li>
<span class="date">2025-05-28</span>
<a href="/showcase.html?Id=000002">某公司劳动纠纷案例二</a>
</li>
<li>
<span class="date">2025-05-28</span>
<a href="/showcase.html?Id=000003">某公司劳动纠纷案例三</a>
</li>
</ul>
</div>
</div>
<!-- 表单下载 -->
<div class="tab-pane fade" id="form" role="tabpanel" aria-labelledby="form-tab">
<div class="page-section">
<ul class="page-list" id="formList">
<li>
<span class="date">2025-05-28</span>
<a href="/table.html?Id=000001">劳动仲裁申请表</a>
</li>
<li>
<span class="date">2025-05-28</span>
<a href="/table.html?Id=000002">劳动合同模板</a>
</li>
<li>
<span class="date">2025-05-28</span>
<a href="/table.html?Id=000003">工伤认定申请表</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="row text-center mt-5">
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">法律规定</h5>
<p class="card-text">查看最新的法律法规和相关规定</p>
<a href="/hasfjlaw" class="btn btn-success">查看详情</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">典型案例</h5>
<p class="card-text">浏览各类典型法律案例分析</p>
<a href="/hasfjcase" class="btn btn-primary">查看详情</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">表单下载</h5>
<p class="card-text">下载各类法律相关表单</p>
<a href="/hasfjform" class="btn btn-danger">查看详情</a>
</div>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script>
$(function() {
// 加载法律规定列表
$.ajax({
url: '/api/hasfj/laws',
type: 'GET',
success: function(res) {
if(res.code === 200 && res.data) {
var html = '';
$.each(res.data, function(index, item) {
var date = new Date(item.createTime).toLocaleDateString();
html += '<li><span class="date">' + date + '</span><a href="/show.html?Id=' + item.formatId + '">' + item.title + '</a></li>';
});
if(html) {
$('#lawList').html(html);
}
}
}
});
// 加载典型案例列表
$.ajax({
url: '/api/hasfj/cases',
type: 'GET',
success: function(res) {
if(res.code === 200 && res.data) {
var html = '';
$.each(res.data, function(index, item) {
var date = new Date(item.createTime).toLocaleDateString();
html += '<li><span class="date">' + date + '</span><a href="/showcase.html?Id=' + item.formatId + '">' + item.title + '</a></li>';
});
if(html) {
$('#caseList').html(html);
}
}
}
});
// 加载表单下载列表
$.ajax({
url: '/api/hasfj/forms',
type: 'GET',
success: function(res) {
if(res.code === 200 && res.data) {
var html = '';
$.each(res.data, function(index, item) {
var date = new Date(item.createTime).toLocaleDateString();
html += '<li><span class="date">' + date + '</span><a href="/table.html?Id=' + item.formatId + '">' + item.title + '</a></li>';
});
if(html) {
$('#formList').html(html);
}
}
}
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title th:text="${page.title}">法律规定</title>
<meta name="keywords" th:content="${page.keywords}" content="法律规定,淮安市司法局">
<meta name="description" th:content="${page.description}" content="淮安市司法局法律规定详情页">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.law-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 5px;
}
.law-title {
text-align: center;
padding: 20px 0;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.law-meta {
color: #666;
font-size: 14px;
margin-bottom: 20px;
text-align: center;
}
.law-content {
line-height: 1.8;
font-size: 16px;
}
</style>
</head>
<body>
<div class="container">
<div class="law-container">
<div class="law-title">
<h2 th:text="${page.title}">法律规定标题</h2>
</div>
<div class="law-meta">
<span>发布时间:<span th:text="${#dates.format(page.createTime, 'yyyy-MM-dd')}">2025-05-28</span></span>
<span class="mx-3">|</span>
<span>作者:<span th:text="${page.author}">管理员</span></span>
<span class="mx-3">|</span>
<span>浏览次数:<span th:text="${page.viewCount}">0</span></span>
</div>
<div class="law-content" th:utext="${page.content}">
<!-- 这里将显示HTML内容 -->
<p>法律规定内容将在这里显示...</p>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>法律规定列表 - 淮安市司法局</title>
<meta name="keywords" content="法律规定,淮安市司法局">
<meta name="description" content="淮安市司法局法律规定列表">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/common.css">
<style>
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.page-header {
text-align: center;
margin-bottom: 30px;
}
.page-section {
margin-bottom: 40px;
}
.page-section h3 {
border-bottom: 2px solid #28a745;
padding-bottom: 10px;
margin-bottom: 20px;
}
.page-list {
list-style: none;
padding: 0;
}
.page-list li {
padding: 12px 0;
border-bottom: 1px dashed #eee;
}
.page-list li .date {
float: right;
color: #999;
}
.page-list li a {
color: #333;
text-decoration: none;
}
.page-list li a:hover {
color: #28a745;
}
</style>
</head>
<body>
<div class="container">
<div class="page-container">
<div class="page-header">
<h2>法律规定</h2>
</div>
<div class="page-section">
<ul class="page-list">
<th:block th:if="${not #lists.isEmpty(lawList)}">
<li th:each="law : ${lawList}">
<span class="date" th:text="${#dates.format(law.createTime, 'yyyy-MM-dd')}">2025-05-28</span>
<a th:href="@{'/show.html?Id=' + ${law.formatId}}" th:text="${law.title}">法律规定标题</a>
</li>
</th:block>
<th:block th:if="${#lists.isEmpty(lawList)}">
<li>暂无相关法律规定</li>
</th:block>
</ul>
</div>
<div class="text-center mt-4">
<a href="/hasfj" class="btn btn-primary">返回首页</a>
</div>
</div>
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,32 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
server: {
port: 80,
proxy: {
'/dev-api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/dev-api/, '')
},
'/profile': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
},
plugins: [
vue(),
vueJsx(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyue-auth</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-auth-common</artifactId>
<description>
system系统模块
</description>
<dependencies>
<!-- 核心模块-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-framework</artifactId>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,293 @@
package com.boyue.auth.common.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.boyue.common.annotation.Excel;
import com.boyue.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* 第三方认证对象 oauth_user
*
* @author Dftre
* @date 2024-01-18
*/
@Schema(description = "第三方认证对象")
public class OauthUser extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 主键 */
@Schema(title = "主键")
private Long id;
/** 第三方系统的唯一ID详细解释请参考名词解释 */
@Schema(title = "第三方系统的唯一ID详细解释请参考名词解释")
@Excel(name = "第三方系统的唯一ID详细解释请参考名词解释")
private String uuid;
/** 用户ID */
@Schema(title = "用户ID")
@Excel(name = "用户ID")
private Long userId;
/**
* 第三方用户来源可选值GITHUBGITEEQQ更多请参考AuthDefaultSource.java(opens new window)
*/
@Schema(title = "第三方用户来源可选值GITHUB、GITEE、QQ更多请参考AuthDefaultSource.java(opens new window)")
@Excel(name = "第三方用户来源可选值GITHUB、GITEE、QQ更多请参考AuthDefaultSource.java(opens new window)")
private String source;
/** 用户的授权令牌 */
@Schema(title = "用户的授权令牌")
@Excel(name = "用户的授权令牌")
private String accessToken;
/** 第三方用户的授权令牌的有效期,部分平台可能没有 */
@Schema(title = "第三方用户的授权令牌的有效期,部分平台可能没有")
@Excel(name = "第三方用户的授权令牌的有效期,部分平台可能没有")
private Long expireIn;
/** 刷新令牌,部分平台可能没有 */
@Schema(title = "刷新令牌,部分平台可能没有")
@Excel(name = "刷新令牌,部分平台可能没有")
private String refreshToken;
/** 第三方用户的 open id部分平台可能没有 */
@Schema(title = "第三方用户的 open id部分平台可能没有")
@Excel(name = "第三方用户的 open id部分平台可能没有")
private String openId;
/** 第三方用户的 ID部分平台可能没有 */
@Schema(title = "第三方用户的 ID部分平台可能没有")
@Excel(name = "第三方用户的 ID部分平台可能没有")
private String uid;
/** 个别平台的授权信息,部分平台可能没有 */
@Schema(title = "个别平台的授权信息,部分平台可能没有")
@Excel(name = "个别平台的授权信息,部分平台可能没有")
private String accessCode;
/** 第三方用户的 union id部分平台可能没有 */
@Schema(title = "第三方用户的 union id部分平台可能没有")
@Excel(name = "第三方用户的 union id部分平台可能没有")
private String unionId;
/** 第三方用户授予的权限,部分平台可能没有 */
@Schema(title = "第三方用户授予的权限,部分平台可能没有")
@Excel(name = "第三方用户授予的权限,部分平台可能没有")
private String scope;
/** 个别平台的授权信息,部分平台可能没有 */
@Schema(title = "个别平台的授权信息,部分平台可能没有")
@Excel(name = "个别平台的授权信息,部分平台可能没有")
private String tokenType;
/** id token部分平台可能没有 */
@Schema(title = "id token部分平台可能没有")
@Excel(name = "id token部分平台可能没有")
private String idToken;
/** 小米平台用户的附带属性,部分平台可能没有 */
@Schema(title = "小米平台用户的附带属性,部分平台可能没有")
@Excel(name = "小米平台用户的附带属性,部分平台可能没有")
private String macAlgorithm;
/** 小米平台用户的附带属性,部分平台可能没有 */
@Schema(title = "小米平台用户的附带属性,部分平台可能没有")
@Excel(name = "小米平台用户的附带属性,部分平台可能没有")
private String macKey;
/** 用户的授权code部分平台可能没有 */
@Schema(title = "用户的授权code部分平台可能没有")
@Excel(name = "用户的授权code部分平台可能没有")
private String code;
/** Twitter平台用户的附带属性部分平台可能没有 */
@Schema(title = "Twitter平台用户的附带属性部分平台可能没有")
@Excel(name = "Twitter平台用户的附带属性部分平台可能没有")
private String oauthToken;
/** Twitter平台用户的附带属性部分平台可能没有 */
@Schema(title = "Twitter平台用户的附带属性部分平台可能没有")
@Excel(name = "Twitter平台用户的附带属性部分平台可能没有")
private String oauthTokenSecret;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getUuid() {
return uuid;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getUserId() {
return userId;
}
public void setSource(String source) {
this.source = source;
}
public String getSource() {
return source;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getAccessToken() {
return accessToken;
}
public void setExpireIn(Long expireIn) {
this.expireIn = expireIn;
}
public Long getExpireIn() {
return expireIn;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getOpenId() {
return openId;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUid() {
return uid;
}
public void setAccessCode(String accessCode) {
this.accessCode = accessCode;
}
public String getAccessCode() {
return accessCode;
}
public void setUnionId(String unionId) {
this.unionId = unionId;
}
public String getUnionId() {
return unionId;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getScope() {
return scope;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getTokenType() {
return tokenType;
}
public void setIdToken(String idToken) {
this.idToken = idToken;
}
public String getIdToken() {
return idToken;
}
public void setMacAlgorithm(String macAlgorithm) {
this.macAlgorithm = macAlgorithm;
}
public String getMacAlgorithm() {
return macAlgorithm;
}
public void setMacKey(String macKey) {
this.macKey = macKey;
}
public String getMacKey() {
return macKey;
}
public void setCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public void setOauthToken(String oauthToken) {
this.oauthToken = oauthToken;
}
public String getOauthToken() {
return oauthToken;
}
public void setOauthTokenSecret(String oauthTokenSecret) {
this.oauthTokenSecret = oauthTokenSecret;
}
public String getOauthTokenSecret() {
return oauthTokenSecret;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("uuid", getUuid())
.append("userId", getUserId())
.append("source", getSource())
.append("accessToken", getAccessToken())
.append("expireIn", getExpireIn())
.append("refreshToken", getRefreshToken())
.append("openId", getOpenId())
.append("uid", getUid())
.append("accessCode", getAccessCode())
.append("unionId", getUnionId())
.append("scope", getScope())
.append("tokenType", getTokenType())
.append("idToken", getIdToken())
.append("macAlgorithm", getMacAlgorithm())
.append("macKey", getMacKey())
.append("code", getCode())
.append("oauthToken", getOauthToken())
.append("oauthTokenSecret", getOauthTokenSecret())
.toString();
}
}

View File

@ -0,0 +1,59 @@
package com.boyue.auth.common.enums;
public enum OauthVerificationUse {
/** 用于登录 */
LOGIN("登录", "login"),
/** 用于注册 */
REGISTER("注册", "register"),
/** 用于禁用 */
DISABLE("禁用", "disable"),
/** 用于重置信息 */
RESET("重置", "reset"),
/** 用于绑定信息 */
BIND("绑定", "bind"),
/** 其他用途 */
OTHER("其他", "other");
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public static OauthVerificationUse getByValue(String value) {
for (OauthVerificationUse use : OauthVerificationUse.values()) {
if (use.getValue().equals(value)) {
return use;
}
}
return null;
}
public static OauthVerificationUse getByName(String name) {
for (OauthVerificationUse use : OauthVerificationUse.values()) {
if (use.getName().equals(name)) {
return use;
}
}
return null;
}
private OauthVerificationUse(String name, String value) {
this.name = name;
this.value = value;
}
}

View File

@ -0,0 +1,112 @@
package com.boyue.auth.common.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.boyue.auth.common.domain.OauthUser;
/**
* 第三方认证Mapper接口
*
* @author Dftre
* @date 2024-01-18
*/
public interface OauthUserMapper {
/**
* 查询第三方认证
*
* @param id 第三方认证主键
* @return 第三方认证
*/
public OauthUser selectOauthUserById(Long id);
public OauthUser selectOauthUserByUserId(Long userId);
/**
* 查询第三方认证
* 钉钉抖音uuid 为用户的 unionid
* 微信公众平台登录京东酷家乐美团uuid 为用户的 openId
* 微信开放平台登录QQuuid 为用户的 openId平台支持获取unionid unionid AuthToken
* 如果支持在登录完成后可以通过 response.getData().getToken().getUnionId() 获取
* Googleuuid 为用户的 subsub为Google的所有账户体系中用户唯一的身份标识符详见OpenID Connect
*
* @param uuid
* @return
*/
public OauthUser selectOauthUserByUUID(String uuid);
/**
* 查询第三方认证列表
*
* @param oauthUser 第三方认证
* @return 第三方认证集合
*/
public List<OauthUser> selectOauthUserList(OauthUser oauthUser);
/**
* 新增第三方认证
*
* @param oauthUser 第三方认证
* @return 结果
*/
public int insertOauthUser(OauthUser oauthUser);
/**
* 修改第三方认证
*
* @param oauthUser 第三方认证
* @return 结果
*/
public int updateOauthUser(OauthUser oauthUser);
/**
* 删除第三方认证
*
* @param id 第三方认证主键
* @return 结果
*/
public int deleteOauthUserById(Long id);
/**
* 批量删除第三方认证
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteOauthUserByIds(Long[] ids);
/**
* 校验source平台是否绑定
*
* @param userId 用户编号
* @param source 绑定平台
* @return 结果
*/
public int checkAuthUser(@Param("userId") Long userId, @Param("source") String source);
/**
* 校验用户名称是否唯一
*
* @param userName 用户名称
* @return 结果
*/
public int checkUserNameUnique(String userName);
/**
* 校验手机号码是否唯一
*
* @param phonenumber 手机号码
* @return 结果
*/
public int checkPhoneUnique(String phonenumber);
/**
* 校验email是否唯一
*
* @param email 用户邮箱
* @return 结果
*/
public int checkEmailUnique(String email);
}

View File

@ -0,0 +1,102 @@
package com.boyue.auth.common.service;
import java.util.List;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.common.core.domain.entity.SysUser;
/**
* 第三方认证Service接口
*
* @author Dftre
* @date 2024-01-18
*/
public interface IOauthUserService {
/**
* 查询第三方认证
*
* @param id 第三方认证主键
* @return 第三方认证
*/
public OauthUser selectOauthUserById(Long id);
public OauthUser selectOauthUserByUUID(String uuid);
public OauthUser selectOauthUserByUserId(Long userId);
public SysUser selectSysUserByUUID(String uuid);
/**
* 查询第三方认证列表
*
* @param oauthUser 第三方认证
* @return 第三方认证集合
*/
public List<OauthUser> selectOauthUserList(OauthUser oauthUser);
/**
* 新增第三方认证
*
* @param oauthUser 第三方认证
* @return 结果
*/
public int insertOauthUser(OauthUser oauthUser);
/**
* 修改第三方认证
*
* @param oauthUser 第三方认证
* @return 结果
*/
public int updateOauthUser(OauthUser oauthUser);
/**
* 批量删除第三方认证
*
* @param ids 需要删除的第三方认证主键集合
* @return 结果
*/
public int deleteOauthUserByIds(Long[] ids);
/**
* 删除第三方认证信息
*
* @param id 第三方认证主键
* @return 结果
*/
public int deleteOauthUserById(Long id);
/**
* 校验source平台是否绑定
*
* @param userId 用户编号
* @param source 绑定平台
* @return 结果
*/
public boolean checkAuthUser(Long userId, String source);
/**
* 校验用户名称是否唯一
*
* @param userName 用户名称
* @return 结果
*/
public boolean checkUserNameUnique(String userName);
/**
* 校验手机号码是否唯一
*
* @param phonenumber 手机号码
* @return 结果
*/
public boolean checkPhoneUnique(String phonenumber);
/**
* 校验email是否唯一
*
* @param email 用户邮箱
* @return 结果
*/
public boolean checkEmailUnique(String email);
}

View File

@ -0,0 +1,15 @@
package com.boyue.auth.common.service;
import com.boyue.auth.common.enums.OauthVerificationUse;
/**
* code认证方式接口
*
* @author zlh
* @date 2024-04-16
*/
public interface OauthVerificationCodeService {
public boolean sendCode(String o, String code,OauthVerificationUse use) throws Exception;
public boolean checkCode(String o, String code,OauthVerificationUse use) throws Exception;
}

View File

@ -0,0 +1,73 @@
package com.boyue.auth.common.service;
import com.boyue.common.core.domain.model.LoginBody;
import com.boyue.common.core.domain.model.RegisterBody;
/**
* 双因素认证TFA操作的服务接口
* 该接口提供处理TFA绑定注册和登录流程的方法
* 包括它们的验证步骤
*
* <p>
* 双因素认证通过要求用户提供两种不同的认证因素
* 为认证过程增加了额外的安全层
* </p>
*/
public interface TfaService {
/**
* 启动将TFA方法绑定到用户账户的流程
*
* @param loginBody 包含TFA绑定所需数据的登录信息
*/
public void doBind(LoginBody loginBody);
/**
* 使用验证码或其他确认方式验证TFA绑定流程
*
* @param loginBody 包含验证数据的登录信息
*/
public void doBindVerify(LoginBody loginBody);
/**
* 处理包含TFA设置的注册流程
*
* @param registerBody 包含用户详情和TFA设置的注册信息
*/
public void doRegister(RegisterBody registerBody);
/**
* 验证包含TFA设置的注册流程
*
* @param registerBody 包含验证数据的注册信息
*/
public void doRegisterVerify(RegisterBody registerBody);
/**
* 启动TFA登录流程的第一步
*
* @param loginBody 包含用户凭证的登录信息
*/
public void doLogin(LoginBody loginBody);
/**
* 验证TFA登录流程的第二步并完成认证
*
* @param loginBody 包含TFA验证码的登录信息
* @return 已认证会话的字符串令牌或会话标识符
*/
public String doLoginVerify(LoginBody loginBody);
/**
* 启动TFA重置流程的第一步
*
* @param registerBody 包含用户凭证的注册信息
*/
public void doReset(RegisterBody registerBody);
/**
* 验证TFA重置流程的第二步并完成重置
*
* @param registerBody 包含TFA验证码的注册信息
*/
public void doResetVerify(RegisterBody registerBody);
}

View File

@ -0,0 +1,149 @@
package com.boyue.auth.common.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.auth.common.mapper.OauthUserMapper;
import com.boyue.auth.common.service.IOauthUserService;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.system.mapper.SysUserMapper;
/**
* 第三方认证Service业务层处理
*
* @author Dftre
* @date 2024-01-18
*/
@Service
public class OauthUserServiceImpl implements IOauthUserService {
@Autowired
private OauthUserMapper oauthUserMapper;
@Autowired
private SysUserMapper sysUserMapper;
/**
* 查询第三方认证
*
* @param id 第三方认证主键
* @return 第三方认证
*/
@Override
public OauthUser selectOauthUserById(Long id) {
return oauthUserMapper.selectOauthUserById(id);
}
@Override
public OauthUser selectOauthUserByUUID(String uuid) {
return oauthUserMapper.selectOauthUserByUUID(uuid);
}
@Override
public OauthUser selectOauthUserByUserId(Long userId) {
return oauthUserMapper.selectOauthUserByUserId(userId);
}
/**
* 查询第三方认证列表
*
* @param oauthUser 第三方认证
* @return 第三方认证
*/
@Override
public List<OauthUser> selectOauthUserList(OauthUser oauthUser) {
return oauthUserMapper.selectOauthUserList(oauthUser);
}
/**
* 新增第三方认证
*
* @param oauthUser 第三方认证
* @return 结果
*/
@Override
public int insertOauthUser(OauthUser oauthUser) {
return oauthUserMapper.insertOauthUser(oauthUser);
}
/**
* 修改第三方认证
*
* @param oauthUser 第三方认证
* @return 结果
*/
@Override
public int updateOauthUser(OauthUser oauthUser) {
return oauthUserMapper.updateOauthUser(oauthUser);
}
/**
* 批量删除第三方认证
*
* @param ids 需要删除的第三方认证主键
* @return 结果
*/
@Override
public int deleteOauthUserByIds(Long[] ids) {
return oauthUserMapper.deleteOauthUserByIds(ids);
}
/**
* 删除第三方认证信息
*
* @param id 第三方认证主键
* @return 结果
*/
@Override
public int deleteOauthUserById(Long id) {
return oauthUserMapper.deleteOauthUserById(id);
}
public SysUser selectSysUserByUUID(String uuid) {
OauthUser oauthUser = oauthUserMapper.selectOauthUserByUUID(uuid);
return sysUserMapper.selectUserById(oauthUser.getUserId());
}
/**
* 校验source平台是否绑定
*
* @param userId 用户编号
* @param source 绑定平台
* @return 结果
*/
public boolean checkAuthUser(Long userId, String source) {
return oauthUserMapper.checkAuthUser(userId, source) > 0;
};
/**
* 校验用户名称是否唯一
*
* @param userName 用户名称
* @return 结果
*/
public boolean checkUserNameUnique(String userName) {
return oauthUserMapper.checkUserNameUnique(userName) > 0;
};
/**
* 校验手机号码是否唯一
*
* @param phonenumber 手机号码
* @return 结果
*/
public boolean checkPhoneUnique(String phonenumber) {
return oauthUserMapper.checkPhoneUnique(phonenumber) > 0;
};
/**
* 校验email是否唯一
*
* @param email 用户邮箱
* @return 结果
*/
public boolean checkEmailUnique(String email) {
return oauthUserMapper.checkEmailUnique(email) > 0;
};
}

View File

@ -0,0 +1,28 @@
package com.boyue.auth.common.utils;
import java.security.SecureRandom;
public class RandomCodeUtil {
public static String randomString(String characters, int length) {
StringBuilder result = new StringBuilder();
SecureRandom random = new SecureRandom();
for (int i = 0; i < length; i++) {
int index = random.nextInt(characters.length());
result.append(characters.charAt(index));
}
return result.toString();
}
public static String numberCode(int length) {
String characters = "0123456789";
return randomString(characters, length);
}
public static String code(int length) {
String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return randomString(characters, length);
}
}

View File

@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.boyue.auth.common.mapper.OauthUserMapper">
<resultMap type="OauthUser" id="OauthUserResult">
<result property="id" column="id" />
<result property="uuid" column="uuid" />
<result property="userId" column="user_id" />
<result property="source" column="source" />
<result property="accessToken" column="access_token" />
<result property="expireIn" column="expire_in" />
<result property="refreshToken" column="refresh_token" />
<result property="openId" column="open_id" />
<result property="uid" column="uid" />
<result property="accessCode" column="access_code" />
<result property="unionId" column="union_id" />
<result property="scope" column="scope" />
<result property="tokenType" column="token_type" />
<result property="idToken" column="id_token" />
<result property="macAlgorithm" column="mac_algorithm" />
<result property="macKey" column="mac_key" />
<result property="code" column="code" />
<result property="oauthToken" column="oauth_token" />
<result property="oauthTokenSecret" column="oauth_token_secret" />
</resultMap>
<sql id="selectOauthUserVo">
select id, uuid, user_id, source, access_token, expire_in, refresh_token, open_id, uid, access_code, union_id, scope, token_type, id_token, mac_algorithm, mac_key, code, oauth_token, oauth_token_secret from oauth_user
</sql>
<select id="checkUserNameUnique" parameterType="String" resultType="int">
select count(1) from sys_user where user_name = #{userName} and del_flag = '0' limit 1
</select>
<select id="checkPhoneUnique" parameterType="String" resultType="int">
select count(1) from sys_user where phonenumber = #{phonenumber} and del_flag = '0' limit 1
</select>
<select id="checkEmailUnique" parameterType="String" resultType="int">
select count(1) from sys_user where email = #{email} and del_flag = '0' limit 1
</select>
<select id="checkAuthUser" parameterType="OauthUser" resultType="int">
select count(1) from oauth_user where user_id=#{userId} and source=#{source} limit 1
</select>
<select id="selectOauthUserList" parameterType="OauthUser" resultMap="OauthUserResult">
<include refid="selectOauthUserVo"/>
<where>
<if test="uuid != null and uuid != ''"> and uuid = #{uuid}</if>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="source != null and source != ''"> and source = #{source}</if>
<if test="accessToken != null and accessToken != ''"> and access_token = #{accessToken}</if>
<if test="expireIn != null "> and expire_in = #{expireIn}</if>
<if test="refreshToken != null and refreshToken != ''"> and refresh_token = #{refreshToken}</if>
<if test="openId != null and openId != ''"> and open_id = #{openId}</if>
<if test="uid != null and uid != ''"> and uid = #{uid}</if>
<if test="accessCode != null and accessCode != ''"> and access_code = #{accessCode}</if>
<if test="unionId != null and unionId != ''"> and union_id = #{unionId}</if>
<if test="scope != null and scope != ''"> and scope = #{scope}</if>
<if test="tokenType != null and tokenType != ''"> and token_type = #{tokenType}</if>
<if test="idToken != null and idToken != ''"> and id_token = #{idToken}</if>
<if test="macAlgorithm != null and macAlgorithm != ''"> and mac_algorithm = #{macAlgorithm}</if>
<if test="macKey != null and macKey != ''"> and mac_key = #{macKey}</if>
<if test="code != null and code != ''"> and code = #{code}</if>
<if test="oauthToken != null and oauthToken != ''"> and oauth_token = #{oauthToken}</if>
<if test="oauthTokenSecret != null and oauthTokenSecret != ''"> and oauth_token_secret = #{oauthTokenSecret}</if>
</where>
</select>
<select id="selectOauthUserById" parameterType="Long" resultMap="OauthUserResult">
<include refid="selectOauthUserVo"/>
where oauth_user.id = #{id}
</select>
<select id="selectOauthUserByUserId" parameterType="Long" resultMap="OauthUserResult">
<include refid="selectOauthUserVo"/>
where oauth_user.user_id = #{user_id}
</select>
<select id="selectOauthUserByUUID" parameterType="String" resultMap="OauthUserResult">
<include refid="selectOauthUserVo"/>
where oauth_user.uuid = #{uuid}
</select>
<insert id="insertOauthUser" parameterType="OauthUser">
insert into oauth_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="uuid != null and uuid != ''">uuid,</if>
<if test="userId != null">user_id,</if>
<if test="source != null and source != ''">source,</if>
<if test="accessToken != null and accessToken != ''">access_token,</if>
<if test="expireIn != null">expire_in,</if>
<if test="refreshToken != null">refresh_token,</if>
<if test="openId != null">open_id,</if>
<if test="uid != null">uid,</if>
<if test="accessCode != null">access_code,</if>
<if test="unionId != null">union_id,</if>
<if test="scope != null">scope,</if>
<if test="tokenType != null">token_type,</if>
<if test="idToken != null">id_token,</if>
<if test="macAlgorithm != null">mac_algorithm,</if>
<if test="macKey != null">mac_key,</if>
<if test="code != null">code,</if>
<if test="oauthToken != null">oauth_token,</if>
<if test="oauthTokenSecret != null">oauth_token_secret,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="uuid != null and uuid != ''">#{uuid},</if>
<if test="userId != null">#{userId},</if>
<if test="source != null and source != ''">#{source},</if>
<if test="accessToken != null and accessToken != ''">#{accessToken},</if>
<if test="expireIn != null">#{expireIn},</if>
<if test="refreshToken != null">#{refreshToken},</if>
<if test="openId != null">#{openId},</if>
<if test="uid != null">#{uid},</if>
<if test="accessCode != null">#{accessCode},</if>
<if test="unionId != null">#{unionId},</if>
<if test="scope != null">#{scope},</if>
<if test="tokenType != null">#{tokenType},</if>
<if test="idToken != null">#{idToken},</if>
<if test="macAlgorithm != null">#{macAlgorithm},</if>
<if test="macKey != null">#{macKey},</if>
<if test="code != null">#{code},</if>
<if test="oauthToken != null">#{oauthToken},</if>
<if test="oauthTokenSecret != null">#{oauthTokenSecret},</if>
</trim>
</insert>
<update id="updateOauthUser" parameterType="OauthUser">
update oauth_user
<trim prefix="SET" suffixOverrides=",">
<if test="uuid != null and uuid != ''">uuid = #{uuid},</if>
<if test="userId != null">user_id = #{userId},</if>
<if test="source != null and source != ''">source = #{source},</if>
<if test="accessToken != null and accessToken != ''">access_token = #{accessToken},</if>
<if test="expireIn != null">expire_in = #{expireIn},</if>
<if test="refreshToken != null">refresh_token = #{refreshToken},</if>
<if test="openId != null">open_id = #{openId},</if>
<if test="uid != null">uid = #{uid},</if>
<if test="accessCode != null">access_code = #{accessCode},</if>
<if test="unionId != null">union_id = #{unionId},</if>
<if test="scope != null">scope = #{scope},</if>
<if test="tokenType != null">token_type = #{tokenType},</if>
<if test="idToken != null">id_token = #{idToken},</if>
<if test="macAlgorithm != null">mac_algorithm = #{macAlgorithm},</if>
<if test="macKey != null">mac_key = #{macKey},</if>
<if test="code != null">code = #{code},</if>
<if test="oauthToken != null">oauth_token = #{oauthToken},</if>
<if test="oauthTokenSecret != null">oauth_token_secret = #{oauthTokenSecret},</if>
</trim>
where oauth_user.id = #{id}
</update>
<delete id="deleteOauthUserById" parameterType="Long">
delete from oauth_user where id = #{id}
</delete>
<delete id="deleteOauthUserByIds" parameterType="String">
delete from oauth_user where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyue-auth</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-auth-starter</artifactId>
<description>
第三方认证模块
</description>
<dependencies>
<!-- 第三方认证通用工具-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-common</artifactId>
</dependency>
<!-- justauth通用认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-oauth-justauth</artifactId>
</dependency>
<!-- 微信小程序和公众号认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-oauth-wx</artifactId>
</dependency>
<!-- 手机号认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-tfa-phone</artifactId>
</dependency>
<!-- 邮箱认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-tfa-email</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,109 @@
package com.boyue.auth.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.auth.common.service.IOauthUserService;
import com.boyue.common.annotation.Log;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.page.TableDataInfo;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.utils.poi.ExcelUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 第三方认证Controller
*
* @author Dftre
* @date 2024-01-18
*/
@RestController
@RequestMapping("/system/oauth")
@Tag(name = "【第三方认证】管理")
public class OauthUserController extends BaseController {
@Autowired
private IOauthUserService oauthUserService;
/**
* 查询第三方认证列表
*/
@Operation(summary = "查询第三方认证列表")
@PreAuthorize("@ss.hasPermi('system:oauth:list')")
@GetMapping("/list")
public TableDataInfo list(OauthUser oauthUser) {
startPage();
List<OauthUser> list = oauthUserService.selectOauthUserList(oauthUser);
return getDataTable(list);
}
/**
* 导出第三方认证列表
*/
@Operation(summary = "导出第三方认证列表")
@PreAuthorize("@ss.hasPermi('system:oauth:export')")
@Log(title = "第三方认证", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, OauthUser oauthUser) {
List<OauthUser> list = oauthUserService.selectOauthUserList(oauthUser);
ExcelUtil<OauthUser> util = new ExcelUtil<OauthUser>(OauthUser.class);
util.exportExcel(response, list, "第三方认证数据");
}
/**
* 获取第三方认证详细信息
*/
@Operation(summary = "获取第三方认证详细信息")
@PreAuthorize("@ss.hasPermi('system:oauth:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return success(oauthUserService.selectOauthUserById(id));
}
/**
* 新增第三方认证
*/
@Operation(summary = "新增第三方认证")
@PreAuthorize("@ss.hasPermi('system:oauth:add')")
@Log(title = "第三方认证", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody OauthUser oauthUser) {
return toAjax(oauthUserService.insertOauthUser(oauthUser));
}
/**
* 修改第三方认证
*/
@Operation(summary = "修改第三方认证")
@PreAuthorize("@ss.hasPermi('system:oauth:edit')")
@Log(title = "第三方认证", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody OauthUser oauthUser) {
return toAjax(oauthUserService.updateOauthUser(oauthUser));
}
/**
* 删除第三方认证
*/
@Operation(summary = "删除第三方认证")
@PreAuthorize("@ss.hasPermi('system:oauth:remove')")
@Log(title = "第三方认证", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable(name = "ids") Long[] ids) {
return toAjax(oauthUserService.deleteOauthUserByIds(ids));
}
}

View File

@ -0,0 +1,110 @@
package com.boyue.auth.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.auth.common.service.TfaService;
import com.boyue.common.annotation.Anonymous;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.model.LoginBody;
import com.boyue.common.core.domain.model.RegisterBody;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
@RestController
@RequestMapping("/auth/{channel}") // dySms mail
@Tag(name = "【第三方认证】双因素认证")
public class TfaController extends BaseController {
@Autowired(required = false)
Map<String, TfaService> tfaServiceMap;
@PostConstruct
public void init() {
if (tfaServiceMap == null) {
tfaServiceMap = new HashMap<>();
logger.warn("请注意,没有加载任何双认证服务");
} else {
tfaServiceMap.forEach((k, v) -> {
logger.info("已加载双认证服务 {}", k);
});
}
}
@Operation(summary = "发送注册验证码")
@PostMapping("/send/register")
@Anonymous
public AjaxResult sendRegister(@PathVariable String channel, @RequestBody RegisterBody registerBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doRegister(registerBody);
return success();
}
@Operation(summary = "验证注册验证码")
@PostMapping("/verify/register")
@Anonymous
public AjaxResult verifyRegister(@PathVariable String channel, @RequestBody RegisterBody registerBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doRegisterVerify(registerBody);
return success();
}
@Operation(summary = "发送登录验证码")
@PostMapping("/send/login")
@Anonymous
public AjaxResult sendLogin(@PathVariable String channel, @RequestBody LoginBody loginBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doLogin(loginBody);
return success();
}
@Operation(summary = "验证登录验证码")
@PostMapping("/verify/login")
@Anonymous
public AjaxResult verifyLogin(@PathVariable String channel, @RequestBody LoginBody loginBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
return success(tfaService.doLoginVerify(loginBody));
}
@Operation(summary = "发送绑定验证码")
@PostMapping("/send/bind")
public AjaxResult send(@PathVariable String channel, @RequestBody LoginBody loginBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doBind(loginBody);
return success();
}
@Operation(summary = "验证绑定验证码")
@PostMapping("/verify/bind") // 发送验证码
public AjaxResult verify(@PathVariable String channel, @RequestBody LoginBody loginBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doBindVerify(loginBody);
return success();
}
@Operation(summary = "发送重置验证码")
@PostMapping("/send/reset")
public AjaxResult sendReset(@PathVariable String channel, @RequestBody RegisterBody registerBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doReset(registerBody);
return success();
}
@Operation(summary = "验证重置验证码")
@PostMapping("/verify/reset")
public AjaxResult verifyReset(@PathVariable String channel, @RequestBody RegisterBody registerBody) {
TfaService tfaService = tfaServiceMap.get("auth:service:" + channel);
tfaService.doReset(registerBody);
return success();
}
}

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyue-auth</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-oauth-justauth</artifactId>
<description>
justauth框架认证模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-common</artifactId>
</dependency>
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,180 @@
package com.boyue.oauth.justauth.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson2.JSONObject;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.auth.common.service.IOauthUserService;
import com.boyue.common.annotation.Anonymous;
import com.boyue.common.constant.Constants;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.enums.UserStatus;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.SecurityUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.framework.web.service.SysPermissionService;
import com.boyue.framework.web.service.TokenService;
import com.boyue.oauth.justauth.utils.JustAuthUtils;
import com.boyue.system.service.ISysUserService;
import jakarta.servlet.http.HttpServletRequest;
import me.zhyd.oauth.cache.AuthDefaultStateCache;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
/**
* 第三方认证授权处理
*
* @author boyue
*/
@RestController
@RequestMapping("/system/auth")
public class SysAuthController extends BaseController
{
private AuthStateCache authStateCache;
@Autowired
private ISysUserService userService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
@Autowired
private IOauthUserService oauthUserService;
private final static Map<String, String> auths = new HashMap<String, String>();
{
auths.put("gitee", "{\"clientId\":\"d83265831bf1b09765c27a9fd9860a9fbb2d939b13460454e85d3d551eebb157\",\"clientSecret\":\"23e3d066c85fc5ee6eaad91067ad006bb5475796f9b9c024eaf5bd26acadd7a3\",\"redirectUri\":\"http://127.0.0.1:80/social-login?source=gitee\"}");
auths.put("github", "{\"clientId\":\"Iv1.1be0cdcd71aca63b\",\"clientSecret\":\"0d59d28b43152bc8906011624db37b0fed88d154\",\"redirectUri\":\"http://127.0.0.1:80/social-login?source=github\"}");
authStateCache = AuthDefaultStateCache.INSTANCE;
}
/**
* 认证授权
*
* @param source
* @throws IOException
*/
@GetMapping("/binding/{source}")
@ResponseBody
public AjaxResult authBinding(@PathVariable("source") String source, HttpServletRequest request) throws IOException
{
LoginUser tokenUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(tokenUser) && oauthUserService.checkAuthUser(tokenUser.getUserId(), source))
{
return error(source + "平台账号已经绑定");
}
String obj = auths.get(source);
if (StringUtils.isEmpty(obj))
{
return error(source + "平台账号暂不支持");
}
JSONObject json = JSONObject.parseObject(obj);
AuthRequest authRequest = JustAuthUtils.getAuthRequest(source, json.getString("clientId"), json.getString("clientSecret"), json.getString("redirectUri"), authStateCache);
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
return success(authorizeUrl);
}
/**
* 第三方登录回调
* @param source
* @param callback
* @param request
* @return
*/
@Anonymous
@GetMapping("/social-login/{source}")
public AjaxResult socialLogin(@PathVariable("source") String source, AuthCallback callback, HttpServletRequest request)
{
String obj = auths.get(source);
if (StringUtils.isEmpty(obj))
{
return AjaxResult.error(10002, "第三方平台系统不支持或未提供来源");
}
JSONObject json = JSONObject.parseObject(obj);
AuthRequest authRequest = JustAuthUtils.getAuthRequest(source, json.getString("clientId"), json.getString("clientSecret"), json.getString("redirectUri"), authStateCache);
AuthResponse<AuthUser> response = authRequest.login(callback);
if (response.ok())
{
LoginUser tokenUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(tokenUser))
{
SysUser user = oauthUserService.selectSysUserByUUID(source + response.getData().getUuid());
if (StringUtils.isNotNull(user))
{
String token = tokenService.createToken(SecurityUtils.getLoginUser());
return success().put(Constants.TOKEN, token);
}
// 若已经登录则直接绑定系统账号
OauthUser authUser = new OauthUser();
// SysUser sysUser = new SysUser();
// sysUser.setAvatar(response.getData().getAvatar());
authUser.setUuid(source + response.getData().getUuid());
authUser.setUserId(SecurityUtils.getUserId());
// sysUser.setUserName(response.getData().getUsername());
// sysUser.setNickName(response.getData().getNickname());
// sysUser.setEmail(response.getData().getEmail());
authUser.setSource(source);
oauthUserService.insertOauthUser(authUser);
// userService.insertUser(sysUser);
String token = tokenService.createToken(SecurityUtils.getLoginUser());
return success().put(Constants.TOKEN, token);
}
SysUser authUser = oauthUserService.selectSysUserByUUID(source + response.getData().getUuid());
if (StringUtils.isNotNull(authUser))
{
SysUser user = userService.selectUserByUserName(authUser.getUserName());
if (StringUtils.isNull(user))
{
throw new ServiceException("登录用户:" + user.getUserName() + " 不存在");
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
throw new ServiceException("对不起,您的账号:" + user.getUserName() + " 已被删除");
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
throw new ServiceException("对不起,您的账号:" + user.getUserName() + " 已停用");
}
LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
String token = tokenService.createToken(loginUser);
return success().put(Constants.TOKEN, token);
}
else
{
return AjaxResult.error(10002, "对不起,您没有绑定注册用户,请先注册后在个人中心绑定第三方授权信息!");
}
}
return AjaxResult.error(10002, "对不起,授权信息验证不通过,请联系管理员");
}
/**
* 取消授权
*/
@DeleteMapping(value = "/unlock/{authId}")
public AjaxResult unlockAuth(@PathVariable Long authId)
{
return toAjax(oauthUserService.deleteOauthUserById(authId));
}
}

View File

@ -0,0 +1,177 @@
package com.boyue.oauth.justauth.utils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.AuthAlipayRequest;
import me.zhyd.oauth.request.AuthAliyunRequest;
import me.zhyd.oauth.request.AuthBaiduRequest;
import me.zhyd.oauth.request.AuthCodingRequest;
// import me.zhyd.oauth.request.AuthCsdnRequest;
import me.zhyd.oauth.request.AuthDingTalkRequest;
import me.zhyd.oauth.request.AuthDouyinRequest;
import me.zhyd.oauth.request.AuthElemeRequest;
import me.zhyd.oauth.request.AuthGiteeRequest;
import me.zhyd.oauth.request.AuthGithubRequest;
import me.zhyd.oauth.request.AuthGitlabRequest;
import me.zhyd.oauth.request.AuthHuaweiRequest;
import me.zhyd.oauth.request.AuthKujialeRequest;
import me.zhyd.oauth.request.AuthLinkedinRequest;
import me.zhyd.oauth.request.AuthMeituanRequest;
import me.zhyd.oauth.request.AuthMiRequest;
import me.zhyd.oauth.request.AuthMicrosoftRequest;
import me.zhyd.oauth.request.AuthOschinaRequest;
import me.zhyd.oauth.request.AuthPinterestRequest;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRenrenRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthStackOverflowRequest;
import me.zhyd.oauth.request.AuthTaobaoRequest;
import me.zhyd.oauth.request.AuthTeambitionRequest;
import me.zhyd.oauth.request.AuthToutiaoRequest;
import me.zhyd.oauth.request.AuthWeChatMpRequest;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import me.zhyd.oauth.request.AuthWeiboRequest;
/**
* 认证授权工具类
*
* @author boyue
*/
public class JustAuthUtils
{
@SuppressWarnings("deprecation")
public static AuthRequest getAuthRequest(String source, String clientId, String clientSecret, String redirectUri,
AuthStateCache authStateCache)
{
AuthRequest authRequest = null;
switch (source.toLowerCase())
{
case "dingtalk":
authRequest = new AuthDingTalkRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "baidu":
authRequest = new AuthBaiduRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "github":
authRequest = new AuthGithubRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "gitee":
authRequest = new AuthGiteeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "weibo":
authRequest = new AuthWeiboRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "coding":
authRequest = new AuthCodingRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "oschina":
authRequest = new AuthOschinaRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "alipay":
// 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1所以这儿的回调地址使用的局域网内的ip
authRequest = new AuthAlipayRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.alipayPublicKey("").redirectUri(redirectUri).build(), authStateCache);
break;
case "qq":
authRequest = new AuthQqRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "wechat_open":
authRequest = new AuthWeChatOpenRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
// case "csdn":
// authRequest = new AuthCsdnRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
// .redirectUri(redirectUri).build(), authStateCache);
// break;
case "taobao":
authRequest = new AuthTaobaoRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "douyin":
authRequest = new AuthDouyinRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "linkedin":
authRequest = new AuthLinkedinRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "microsoft":
authRequest = new AuthMicrosoftRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "mi":
authRequest = new AuthMiRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "toutiao":
authRequest = new AuthToutiaoRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "teambition":
authRequest = new AuthTeambitionRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "pinterest":
authRequest = new AuthPinterestRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "renren":
authRequest = new AuthRenrenRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "stack_overflow":
authRequest = new AuthStackOverflowRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).stackOverflowKey("").build(),
authStateCache);
break;
case "huawei":
authRequest = new AuthHuaweiRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
// case "wechat_enterprise":
// authRequest = new AuthWeChatEnterpriseRequest(AuthConfig.builder().clientId(clientId)
// .clientSecret(clientSecret).redirectUri(redirectUri).agentId("").build(), authStateCache);
// break;
case "kujiale":
authRequest = new AuthKujialeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "gitlab":
authRequest = new AuthGitlabRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "meituan":
authRequest = new AuthMeituanRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "eleme":
authRequest = new AuthElemeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build());
break;
case "wechat_mp":
authRequest = new AuthWeChatMpRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "aliyun":
authRequest = new AuthAliyunRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
default:
break;
}
if (null == authRequest)
{
throw new AuthException("未获取到有效的Auth配置");
}
return authRequest;
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyue-auth</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-oauth-wx</artifactId>
<description>
微信认证模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-common</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,42 @@
package com.boyue.oauth.wx.constant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WxMiniAppConstant {
@Value("${oauth.wx.miniapp.appId}")
private String appId;
@Value("${oauth.wx.miniapp.appSecret}")
private String appSecret;
@Value("${oauth.wx.miniapp.url}")
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
}

View File

@ -0,0 +1,41 @@
package com.boyue.oauth.wx.constant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WxPubConstant {
@Value("${oauth.wx.pub.appId}")
private String appId;
@Value("${oauth.wx.pub.appSecret}")
private String appSecret;
@Value("${oauth.wx.pub.url}")
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
}

View File

@ -0,0 +1,69 @@
package com.boyue.oauth.wx.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.auth.common.service.IOauthUserService;
import com.boyue.common.annotation.Anonymous;
import com.boyue.common.constant.Constants;
import com.boyue.common.core.controller.BaseController;
import com.boyue.common.core.domain.AjaxResult;
import com.boyue.common.utils.StringUtils;
import com.boyue.oauth.wx.service.Impl.WxMiniAppLoginServiceImpl;
import com.boyue.oauth.wx.service.Impl.WxPubLoginServiceImpl;
@RestController
@RequestMapping("/oauth/wx")
public class WxLoginController extends BaseController {
@Autowired
private IOauthUserService oauthUserService;
@Autowired
private WxMiniAppLoginServiceImpl wxMiniAppLoginServiceImpl;
@Autowired
private WxPubLoginServiceImpl wxPubLoginServiceImpl;
@Anonymous
@PostMapping("/login/{source}/{code}")
public AjaxResult loginMiniApp(@PathVariable("source") String source, @PathVariable("code") String code) {
String token = null;
AjaxResult ajax = AjaxResult.success();
if ("miniapp".equals(source)) {
token = wxMiniAppLoginServiceImpl.doLogin(code);
} else if ("pub".equals(source)) {
token = wxPubLoginServiceImpl.doLogin(code);
} else {
return error("错误的登录方式");
}
ajax.put(Constants.TOKEN, token);
return ajax;
}
@PostMapping("/register/{source}/{code}")
public AjaxResult register(@PathVariable("source") String source, @PathVariable("code") String code) {
OauthUser oauthUser = oauthUserService.selectOauthUserByUserId(getUserId());
if (oauthUser != null) {
return error("不可以重复绑定");
} else {
String msg = "";
oauthUser = new OauthUser();
oauthUser.setUserId(getUserId());
oauthUser.setCode(code);
if ("miniapp".equals(source)) {
msg = wxMiniAppLoginServiceImpl.doRegister(oauthUser);
} else if ("pub".equals(source)) {
msg = wxPubLoginServiceImpl.doRegister(oauthUser);
} else {
return error("错误的注册方式");
}
return StringUtils.isEmpty(msg) ? success() : error(msg);
}
}
}

View File

@ -0,0 +1,76 @@
package com.boyue.oauth.wx.service.Impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSONObject;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.auth.common.service.IOauthUserService;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.StringUtils;
import com.boyue.framework.web.service.TokenService;
import com.boyue.framework.web.service.UserDetailsServiceImpl;
import com.boyue.oauth.wx.constant.WxMiniAppConstant;
import com.boyue.oauth.wx.service.WxLoginService;
import com.boyue.system.service.ISysUserService;
@Service
public class WxMiniAppLoginServiceImpl implements WxLoginService {
@Autowired
private WxMiniAppConstant wxAppConstant;
@Autowired
private TokenService tokenService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@Autowired
private ISysUserService userService;
@Autowired
private IOauthUserService oauthUserService;
@Override
public String doLogin(String code) {
String openid = doAuth(
wxAppConstant.getUrl(),
wxAppConstant.getAppId(),
wxAppConstant.getAppSecret(),
code).getString("openid");
OauthUser selectOauthUser = oauthUserService.selectOauthUserByUUID(openid);
if (selectOauthUser == null) {
return null;
}
SysUser sysUser = userService.selectUserById(selectOauthUser.getUserId());
if (sysUser == null) {
throw new ServiceException("该微信未绑定用户");
}
LoginUser loginUser = (LoginUser) userDetailsServiceImpl.createLoginUser(sysUser);
return tokenService.createToken(loginUser);
}
@Override
public String doRegister(OauthUser oauthUser) {
if (StringUtils.isEmpty(oauthUser.getCode())) {
return "没有凭证";
}
if (oauthUser.getUserId() == null) {
return "请先注册账号";
}
JSONObject doAuth = doAuth(
wxAppConstant.getUrl(),
wxAppConstant.getAppId(),
wxAppConstant.getAppSecret(),
oauthUser.getCode());
oauthUser.setOpenId(doAuth.getString("openid"));
oauthUser.setUuid(doAuth.getString("openid"));
oauthUser.setSource("WXMiniApp");
oauthUser.setAccessToken(doAuth.getString("sessionKey"));
oauthUserService.insertOauthUser(oauthUser);
return "";
}
}

View File

@ -0,0 +1,77 @@
package com.boyue.oauth.wx.service.Impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSONObject;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.auth.common.service.IOauthUserService;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.StringUtils;
import com.boyue.framework.web.service.TokenService;
import com.boyue.framework.web.service.UserDetailsServiceImpl;
import com.boyue.oauth.wx.constant.WxPubConstant;
import com.boyue.oauth.wx.service.WxLoginService;
import com.boyue.system.service.ISysUserService;
@Service
public class WxPubLoginServiceImpl implements WxLoginService {
@Autowired
private WxPubConstant wxH5Constant;
@Autowired
private TokenService tokenService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@Autowired
private ISysUserService userService;
@Autowired
private IOauthUserService oauthUserService;
@Override
public String doLogin(String code) {
String openid = doAuth(
wxH5Constant.getUrl(),
wxH5Constant.getAppId(),
wxH5Constant.getAppSecret(),
code).getString("openid");
OauthUser selectOauthUser = oauthUserService.selectOauthUserByUUID(openid);
if (selectOauthUser == null) {
return null;
}
SysUser sysUser = userService.selectUserById(selectOauthUser.getUserId());
if (sysUser == null) {
throw new ServiceException("该微信未绑定用户");
}
LoginUser loginUser = (LoginUser) userDetailsServiceImpl.createLoginUser(sysUser);
return tokenService.createToken(loginUser);
}
@Override
public String doRegister(OauthUser oauthUser) {
if (StringUtils.isEmpty(oauthUser.getCode())) {
return "没有凭证";
}
if (oauthUser.getUserId() == null) {
return "请先注册账号";
}
JSONObject doAuth = doAuth(
wxH5Constant.getUrl(),
wxH5Constant.getAppId(),
wxH5Constant.getAppSecret(),
oauthUser.getCode());
oauthUser.setOpenId(doAuth.getString("openid"));
oauthUser.setUuid(doAuth.getString("openid"));
oauthUser.setSource("WXPub");
oauthUser.setAccessToken(doAuth.getString("sessionKey"));
oauthUserService.insertOauthUser(oauthUser);
return "";
}
}

View File

@ -0,0 +1,34 @@
package com.boyue.oauth.wx.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.boyue.auth.common.domain.OauthUser;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.http.HttpClientUtil;
public interface WxLoginService {
public String doLogin(String code);
public String doRegister(OauthUser oauthUser);
public default JSONObject doAuth(String url, String appid, String secret, String code) {
StringBuilder builder = new StringBuilder(url);
builder.append("?appid=").append(appid)
.append("&secret=").append(secret)
.append("&js_code=").append(code)
.append("&grant_type=").append("authorization_code");
String getMessageUrl = builder.toString();
String result = HttpClientUtil.sendHttpGet(getMessageUrl);
JSONObject jsonObject = JSON.parseObject(result);
if (jsonObject.containsKey("openid")) {
String openid = jsonObject.getString("openid");
String sessionKey = jsonObject.getString("session_key");
System.out.println("openid:" + openid);
System.out.println("sessionKey:" + sessionKey);
return jsonObject;
} else {
throw new ServiceException(jsonObject.getString("errmsg"), jsonObject.getIntValue("errcode"));
}
}
}

View File

@ -0,0 +1,34 @@
{
"properties": [
{
"name": "oauth.wx.miniapp.app-id",
"type": "java.lang.String",
"description": "微信小程序appid"
},
{
"name": "oauth.wx.miniapp.app-secret",
"type": "java.lang.String",
"description": "微信小程序appSecret"
},
{
"name": "oauth.wx.miniapp.url",
"type": "java.lang.String",
"description": "微信小程序认证地址"
},
{
"name": "oauth.wx.pub.app-id",
"type": "java.lang.String",
"description": "微信公众号appid"
},
{
"name": "oauth.wx.pub.app-secret",
"type": "java.lang.String",
"description": "微信公众号appSecret"
},
{
"name": "oauth.wx.pub.url",
"type": "java.lang.String",
"description": "微信公众号认证地址"
}
]
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyue-auth</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-tfa-email</artifactId>
<description>
邮箱认证模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,7 @@
package com.boyue.tfa.email.service;
import com.boyue.auth.common.service.OauthVerificationCodeService;
import com.boyue.auth.common.service.TfaService;
public interface IMailService extends OauthVerificationCodeService,TfaService {
}

View File

@ -0,0 +1,174 @@
package com.boyue.tfa.email.service.impl;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.boyue.auth.common.enums.OauthVerificationUse;
import com.boyue.auth.common.utils.RandomCodeUtil;
import com.boyue.common.constant.CacheConstants;
import com.boyue.common.constant.Constants;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginBody;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.core.domain.model.RegisterBody;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.CacheUtils;
import com.boyue.common.utils.MessageUtils;
import com.boyue.common.utils.SecurityUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.framework.manager.AsyncManager;
import com.boyue.framework.manager.factory.AsyncFactory;
import com.boyue.framework.web.service.SysLoginService;
import com.boyue.framework.web.service.TokenService;
import com.boyue.framework.web.service.UserDetailsServiceImpl;
import com.boyue.system.service.ISysUserService;
import com.boyue.tfa.email.service.IMailService;
import com.boyue.tfa.email.utils.EmailUtil;
@Service("auth:service:mail")
public class MailServiceImpl implements IMailService {
@Autowired
private ISysUserService userService;
@Autowired
private TokenService tokenService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@Autowired
private SysLoginService sysLoginService;
private static final Logger log = LoggerFactory.getLogger(MailServiceImpl.class);
@Override
public boolean sendCode(String email, String code, OauthVerificationUse use) {
if (CacheUtils.hasKey(CacheConstants.EMAIL_CODES, use.getValue() + email)) {
throw new ServiceException("当前验证码未失效请在1分钟后再发送");
}
try {
EmailUtil.sendMessage(email, "验证码邮件", "您收到的验证码是:" + code);
CacheUtils.put(CacheConstants.EMAIL_CODES, use.getValue() + email, code, 1, TimeUnit.MINUTES);
log.info("发送邮箱验证码成功:{ email: " + email + " code:" + code + "}");
return true;
} catch (Exception e) {
throw new ServiceException("发送邮箱验证码异常:" + email);
}
}
@Override
public boolean checkCode(String email, String code, OauthVerificationUse use) {
if (StringUtils.isEmpty(code)) {
return false;
}
String cachedCode = CacheUtils.get(CacheConstants.EMAIL_CODES, use.getValue() + email, String.class); // 从缓存中获取验证码
boolean isValid = code.equals(cachedCode);
if (isValid) {
CacheUtils.remove(CacheConstants.EMAIL_CODES, use.getValue() + email);
}
return isValid;
}
@Override
public void doLogin(LoginBody loginBody) {
SysUser sysUser = userService.selectUserByEmail(loginBody.getEmail());
if (sysUser == null) {
throw new ServiceException("该邮箱未绑定用户");
} else {
sendCode(loginBody.getEmail(), RandomCodeUtil.numberCode(6), OauthVerificationUse.LOGIN);
}
}
@Override
public String doLoginVerify(LoginBody loginBody) {
if (checkCode(loginBody.getEmail(), loginBody.getCode(), OauthVerificationUse.LOGIN)) {
SysUser sysUser = userService.selectUserByEmail(loginBody.getEmail());
if (sysUser == null) {
throw new ServiceException("该邮箱未绑定用户");
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS,
MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) userDetailsServiceImpl.createLoginUser(sysUser);
sysLoginService.recordLoginInfo(loginUser.getUserId());
return tokenService.createToken(loginUser);
} else {
throw new ServiceException("验证码错误");
}
}
@Override
public void doRegister(RegisterBody registerBody) {
SysUser sysUser = userService.selectUserByEmail(registerBody.getEmail());
if (sysUser != null) {
throw new ServiceException("该邮箱已绑定用户");
} else {
sendCode(registerBody.getEmail(), RandomCodeUtil.numberCode(6), OauthVerificationUse.REGISTER);
}
}
@Override
public void doRegisterVerify(RegisterBody registerBody) {
if (checkCode(registerBody.getEmail(), registerBody.getCode(), OauthVerificationUse.REGISTER)) {
SysUser sysUser = new SysUser();
sysUser.setUserName(registerBody.getEmail());
sysUser.setNickName(registerBody.getEmail());
sysUser.setPassword(SecurityUtils.encryptPassword(registerBody.getPassword()));
sysUser.setEmail(registerBody.getEmail());
userService.registerUser(sysUser);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.REGISTER,
MessageUtils.message("user.register.success")));
} else {
throw new ServiceException("验证码错误");
}
}
public void doReset(RegisterBody registerBody) {
SysUser sysUser = userService.selectUserByEmail(registerBody.getEmail());
if (sysUser == null) {
throw new ServiceException("该邮箱未绑定用户");
} else {
sendCode(registerBody.getEmail(), RandomCodeUtil.numberCode(6), OauthVerificationUse.RESET);
}
}
public void doResetVerify(RegisterBody registerBody) {
if (checkCode(registerBody.getEmail(), registerBody.getCode(), OauthVerificationUse.RESET)) {
SysUser sysUser = userService.selectUserById(SecurityUtils.getUserId());
sysUser.setEmail(registerBody.getEmail());
userService.updateUser(sysUser);
} else {
throw new ServiceException("验证码错误");
}
}
@Override
public void doBind(LoginBody loginBody) {
SysUser sysUser = userService.selectUserByEmail(loginBody.getEmail());
if (sysUser != null) {
throw new ServiceException("该邮箱已绑定用户");
}
sysUser = userService.selectUserById(SecurityUtils.getUserId());
if (!SecurityUtils.matchesPassword(loginBody.getPassword(), sysUser.getPassword())) {
throw new ServiceException("密码错误");
}
sendCode(loginBody.getEmail(), RandomCodeUtil.numberCode(6), OauthVerificationUse.BIND);
}
@Override
public void doBindVerify(LoginBody loginBody) {
if (checkCode(loginBody.getEmail(), loginBody.getCode(), OauthVerificationUse.BIND)) {
SysUser sysUser = userService.selectUserById(SecurityUtils.getUserId());
if (!SecurityUtils.matchesPassword(loginBody.getPassword(), sysUser.getPassword())) {
throw new ServiceException("密码错误");
}
sysUser.setEmail(loginBody.getEmail());
userService.updateUser(sysUser);
} else {
throw new ServiceException("验证码错误");
}
}
}

View File

@ -0,0 +1,25 @@
package com.boyue.tfa.email.utils;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.spring.SpringUtils;
public class EmailUtil {
public static void sendMessage(String email, String title, String message) {
if (StringUtils.isEmpty(email)) {
throw new ServiceException("邮箱不能为空");
}
MailProperties mailProperties = SpringUtils.getBean(MailProperties.class);
JavaMailSenderImpl mailSender = SpringUtils.getBean(JavaMailSenderImpl.class);
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setSubject(title);
simpleMailMessage.setText(message);
simpleMailMessage.setFrom(mailProperties.getUsername());
simpleMailMessage.setTo(email);
mailSender.send(simpleMailMessage);
}
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyue-auth</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-tfa-phone</artifactId>
<description>
手机号认证模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-common</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,47 @@
package com.boyue.tfa.phone.config;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import com.boyue.tfa.phone.domain.DySmsTemplate;
/**
* 手机号认证数据
*
* @author Dftre
* @date 2024-04-16
*/
@Configuration
@ConfigurationProperties("tfa.phone.dysms")
public class DySmsConfig {
private String accessKeyId;
private String accessKeySecret;
private Map<String, DySmsTemplate> template;
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public Map<String, DySmsTemplate> getTemplate() {
return template;
}
public void setTemplate(Map<String, DySmsTemplate> template) {
this.template = template;
}
}

View File

@ -0,0 +1,46 @@
package com.boyue.tfa.phone.domain;
/**
* 手机号认证短信模板
*
* @author Dftre
* @date 2024-04-16
*/
public class DySmsTemplate {
/**
* 短信模板编码
*/
private String templateCode;
/**
* 签名
*/
private String signName;
/**
* 短信模板必需的数据名称多个key以逗号分隔此处配置作为校验
*/
private String keys;
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
public String getSignName() {
return signName;
}
public void setSignName(String signName) {
this.signName = signName;
}
public String getKeys() {
return keys;
}
public void setKeys(String keys) {
this.keys = keys;
}
}

View File

@ -0,0 +1,14 @@
package com.boyue.tfa.phone.service;
import com.boyue.auth.common.service.OauthVerificationCodeService;
import com.boyue.auth.common.service.TfaService;
/**
* 手机号认证Servcie
*
* @author zlh
* @date 2024-04-16
*/
public interface DySmsService extends OauthVerificationCodeService, TfaService {
}

View File

@ -0,0 +1,185 @@
package com.boyue.tfa.phone.service.Impl;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSONObject;
import com.boyue.auth.common.enums.OauthVerificationUse;
import com.boyue.auth.common.utils.RandomCodeUtil;
import com.boyue.common.constant.CacheConstants;
import com.boyue.common.constant.Constants;
import com.boyue.common.core.domain.entity.SysUser;
import com.boyue.common.core.domain.model.LoginBody;
import com.boyue.common.core.domain.model.LoginUser;
import com.boyue.common.core.domain.model.RegisterBody;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.CacheUtils;
import com.boyue.common.utils.MessageUtils;
import com.boyue.common.utils.SecurityUtils;
import com.boyue.common.utils.StringUtils;
import com.boyue.framework.manager.AsyncManager;
import com.boyue.framework.manager.factory.AsyncFactory;
import com.boyue.framework.web.service.SysLoginService;
import com.boyue.framework.web.service.TokenService;
import com.boyue.framework.web.service.UserDetailsServiceImpl;
import com.boyue.system.service.ISysUserService;
import com.boyue.tfa.phone.config.DySmsConfig;
import com.boyue.tfa.phone.service.DySmsService;
import com.boyue.tfa.phone.utils.DySmsUtil;
/**
* 手机号认证Servcie
*
* @author zlh
* @date 2024-04-16
*/
@Service("auth:service:dySms")
public class DySmsServiceImpl implements DySmsService {
@Autowired
private ISysUserService userService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@Autowired
private TokenService tokenService;
@Autowired
private SysLoginService sysLoginService;
@Autowired
private DySmsConfig dySmsConfig;
private static final Logger log = LoggerFactory.getLogger(DySmsServiceImpl.class);
@Override
public boolean sendCode(String phone, String code, OauthVerificationUse use) {
if (CacheUtils.hasKey(CacheConstants.PHONE_CODES, use.getValue() + phone)) {
throw new ServiceException("当前验证码未失效请在1分钟后再发送");
}
try {
JSONObject templateParams = new JSONObject();
templateParams.put("code", code);
DySmsUtil.sendSms(phone, dySmsConfig.getTemplate().get("VerificationCode"), templateParams);
CacheUtils.put(CacheConstants.PHONE_CODES, use.getValue() + phone, code, 1, TimeUnit.MINUTES);
log.info("发送手机验证码成功:{ phone: " + phone + " code:" + code + "}");
return true;
} catch (Exception e) {
throw new ServiceException("发送手机验证码异常:" + phone);
}
}
@Override
public boolean checkCode(String phone, String code, OauthVerificationUse use) {
if (StringUtils.isEmpty(code)) {
return false;
}
String cachedCode = CacheUtils.get(CacheConstants.PHONE_CODES, use.getValue() + phone, String.class); // 从缓存中获取验证码
boolean isValid = code.equals(cachedCode);
if (isValid) {
CacheUtils.remove(CacheConstants.PHONE_CODES, use.getValue() + phone);
}
return isValid;
}
@Override
public void doLogin(LoginBody loginBody) {
SysUser sysUser = userService.selectUserByPhone(loginBody.getPhonenumber());
if (sysUser == null) {
throw new ServiceException("该手机号未绑定用户");
} else {
sendCode(loginBody.getPhonenumber(), RandomCodeUtil.numberCode(6), OauthVerificationUse.LOGIN);
}
}
@Override
public String doLoginVerify(LoginBody loginBody) {
if (checkCode(loginBody.getPhonenumber(), loginBody.getCode(), OauthVerificationUse.LOGIN)) {
SysUser sysUser = userService.selectUserByPhone(loginBody.getPhonenumber());
if (sysUser == null) {
throw new ServiceException("该手机号未绑定用户");
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS,
MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) userDetailsServiceImpl.createLoginUser(sysUser);
sysLoginService.recordLoginInfo(loginUser.getUserId());
return tokenService.createToken(loginUser);
} else {
throw new ServiceException("验证码错误");
}
}
@Override
public void doRegister(RegisterBody registerBody) {
SysUser sysUser = userService.selectUserByPhone(registerBody.getPhonenumber());
if (sysUser != null) {
throw new ServiceException("该手机号已绑定用户");
} else {
sendCode(registerBody.getPhonenumber(), RandomCodeUtil.numberCode(6), OauthVerificationUse.REGISTER);
}
}
@Override
public void doRegisterVerify(RegisterBody registerBody) {
if (checkCode(registerBody.getPhonenumber(), registerBody.getCode(), OauthVerificationUse.REGISTER)) {
SysUser sysUser = new SysUser();
sysUser.setUserName(registerBody.getPhonenumber());
sysUser.setNickName(registerBody.getUsername());
sysUser.setPassword(SecurityUtils.encryptPassword(registerBody.getPassword()));
sysUser.setPhonenumber(registerBody.getPhonenumber());
userService.registerUser(sysUser);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.REGISTER,
MessageUtils.message("user.register.success")));
} else {
throw new ServiceException("验证码错误");
}
}
public void doReset(RegisterBody registerBody) {
SysUser sysUser = userService.selectUserByPhone(registerBody.getPhonenumber());
if (sysUser == null) {
throw new ServiceException("该手机号未绑定用户");
} else {
sendCode(registerBody.getPhonenumber(), RandomCodeUtil.numberCode(6), OauthVerificationUse.RESET);
}
}
public void doResetVerify(RegisterBody registerBody) {
if (checkCode(registerBody.getPhonenumber(), registerBody.getCode(), OauthVerificationUse.RESET)) {
SysUser sysUser = userService.selectUserById(SecurityUtils.getUserId());
sysUser.setPhonenumber(registerBody.getPhonenumber());
userService.updateUser(sysUser);
} else {
throw new ServiceException("验证码错误");
}
}
@Override
public void doBind(LoginBody loginBody) {
SysUser sysUser = userService.selectUserByPhone(loginBody.getPhonenumber());
if (sysUser != null) {
throw new ServiceException("该手机号已绑定用户");
}
sysUser = userService.selectUserById(SecurityUtils.getUserId());
if (!SecurityUtils.matchesPassword(loginBody.getPassword(), sysUser.getPassword())) {
throw new ServiceException("密码错误");
}
sendCode(loginBody.getPhonenumber(), RandomCodeUtil.numberCode(6), OauthVerificationUse.BIND);
}
@Override
public void doBindVerify(LoginBody loginBody) {
if (checkCode(loginBody.getPhonenumber(), loginBody.getCode(), OauthVerificationUse.BIND)) {
SysUser sysUser = userService.selectUserById(SecurityUtils.getUserId());
if (!SecurityUtils.matchesPassword(loginBody.getPassword(), sysUser.getPassword())) {
throw new ServiceException("密码错误");
}
sysUser.setPhonenumber(loginBody.getPhonenumber());
userService.updateUser(sysUser);
} else {
throw new ServiceException("验证码错误");
}
}
}

View File

@ -0,0 +1,88 @@
package com.boyue.tfa.phone.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.boyue.common.exception.ServiceException;
import com.boyue.common.utils.StringUtils;
import com.boyue.common.utils.spring.SpringUtils;
import com.boyue.tfa.phone.config.DySmsConfig;
import com.boyue.tfa.phone.domain.DySmsTemplate;
public class DySmsUtil {
protected final static Logger logger = LoggerFactory.getLogger(DySmsUtil.class);
/**
* 使用AK&SK初始化账号Client
*
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
private static Client createClient() throws Exception {
DySmsConfig dySmsConfig = SpringUtils.getBean(DySmsConfig.class);
Config config = new Config()
// 必填您的 AccessKey ID
.setAccessKeyId(dySmsConfig.getAccessKeyId())
// 必填您的 AccessKey Secret
.setAccessKeySecret(dySmsConfig.getAccessKeySecret());
config.endpoint = "dysmsapi.aliyuncs.com";
return new Client(config);
}
/**
* 验证参数
*
* @param templateParamJson
* @param dySmsTemplate
* @throws Exception
*/
private static void validateParam(JSONObject templateParamJson, DySmsTemplate dySmsTemplate) {
String keys = dySmsTemplate.getKeys();
String[] keyArr = keys.split(",");
for (String item : keyArr) {
if (!templateParamJson.containsKey(item)) {
throw new RuntimeException("模板缺少参数:" + item);
}
}
}
public static void sendSms(String phone, DySmsTemplate dySmsTemplate, JSONObject templateParamJson)
throws Exception {
if (StringUtils.isEmpty(phone)) {
throw new ServiceException("手机号不能为空");
}
validateParam(templateParamJson, dySmsTemplate);
Client client = createClient();
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(phone)
.setSignName(dySmsTemplate.getSignName())
.setTemplateCode(dySmsTemplate.getTemplateCode())
.setTemplateParam(templateParamJson.toJSONString());
try {
// 复制代码运行请自行打印 API 的返回值
SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, new RuntimeOptions());
if ("OK".equals(sendSmsResponse.getBody().getCode())) {
logger.info("短信接口返回的数据--- {}", sendSmsResponse.getBody().getMessage());
} else {
logger.error("短信接口返回的数据--- {}", sendSmsResponse.getBody().getMessage());
throw new ServiceException(sendSmsResponse.getBody().getMessage());
}
} catch (TeaException error) {
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
Common.assertAsString(error.message);
}
}
}

View File

@ -0,0 +1,19 @@
{
"properties": [
{
"name": "tfa.phone.dysms.access-key-id",
"type": "java.lang.String",
"description": "阿里云短信 AccessKey ID"
},
{
"name": "tfa.phone.dysms.access-key-secret",
"type": "java.lang.String",
"description": "阿里云短信 AccessKey Secret"
},
{
"name": "tfa.phone.dysms.template",
"type": "java.util.Map<java.lang.String, com.boyue.tfa.phone.domain.DySmsTemplate>",
"description": "短信模板"
}
]
}

118
boyue-auth/pom.xml Normal file
View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyuehasfj-java</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-auth</artifactId>
<properties>
<justauth.version>1.16.7</justauth.version>
<alipay.version>3.7.4.ALL</alipay.version>
<dysmsapi.version>3.1.1</dysmsapi.version>
<mail.version>2.0.1</mail.version>
</properties>
<description>
第三方认证模块
</description>
<dependencyManagement>
<dependencies>
<!-- 第三方认证通用工具-->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-common</artifactId>
<version>${boyue.version}</version>
</dependency>
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>${mail.version}</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>${mail.version}</version>
</dependency>
<!-- justauth第三方认证框架 -->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${justauth.version}</version>
</dependency>
<!-- 支付宝开发者sdk -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 阿里云短信认证 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${dysmsapi.version}</version>
</dependency>
<!-- justauth通用认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-oauth-justauth</artifactId>
<version>${boyue.version}</version>
</dependency>
<!-- 微信小程序和公众号认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-oauth-wx</artifactId>
<version>${boyue.version}</version>
</dependency>
<!-- 手机号认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-tfa-phone</artifactId>
<version>${boyue.version}</version>
</dependency>
<!-- 邮箱认证 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-tfa-email</artifactId>
<version>${boyue.version}</version>
</dependency>
<!-- 第三方登录启动器 -->
<dependency>
<groupId>com.boyue</groupId>
<artifactId>boyue-auth-starter</artifactId>
<version>${boyue.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>boyue-auth-common</module>
<module>boyue-oauth-justauth</module>
<module>boyue-oauth-wx</module>
<module>boyue-tfa-phone</module>
<module>boyue-tfa-email</module>
<module>boyue-auth-starter</module>
</modules>
<packaging>pom</packaging>
</project>

146
boyue-common/pom.xml Normal file
View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>boyuehasfj-java</artifactId>
<groupId>com.boyue</groupId>
<version>3.8.9-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>boyue-common</artifactId>
<description>
common通用工具
</description>
<dependencies>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--引入jsr310模块 支持java8的时间序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- SpringCache的依赖配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.boyue.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 匿名访问不鉴权注解
*
* @author boyue
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

View File

@ -0,0 +1,33 @@
package com.boyue.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据权限过滤注解
*
* @author boyue
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
/**
* 权限字符用于多个角色匹配符合要求的权限默认根据权限注解@ss获取多个权限用逗号分隔开来
*/
public String permission() default "";
}

View File

@ -0,0 +1,34 @@
package com.boyue.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.boyue.common.enums.DataSourceType;
/**
* 自定义多数据源切换注解
*
* 优先级先方法后类如果方法覆盖了类上的数据源类型以方法的为准否则以类上的为准
*
* @author boyue
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
/**
* 切换数据源名称 - 枚举方式
*/
public DataSourceType value() default DataSourceType.MASTER;
/**
* 切换数据源名称 - 字符串方式
*/
public String name() default "";
}

View File

@ -0,0 +1,206 @@
package com.boyue.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.boyue.common.utils.poi.ExcelHandlerAdapter;
/**
* 自定义导出Excel数据注解
*
* @author boyue
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
/**
* 导出时在excel中排序
*/
public int sort() default Integer.MAX_VALUE;
/**
* 导出到Excel中的名字.
*/
public String name() default "";
/**
* 日期格式, : yyyy-MM-dd
*/
public String dateFormat() default "";
/**
* 如果是字典类型请设置字典的type值 (: sys_user_sex)
*/
public String dictType() default "";
/**
* 读取内容转表达式 (: 0=,1=,2=未知)
*/
public String readConverterExp() default "";
/**
* 分隔符读取字符串组内容
*/
public String separator() default ",";
/**
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
*/
public int scale() default -1;
/**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
public RoundingMode roundingMode() default RoundingMode.HALF_EVEN;
/**
* 导出时在excel中每个列的高度
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽度
*/
public double width() default 16;
/**
* 文字后缀,% 90 变成90%
*/
public String suffix() default "";
/**
* 当值为空时,字段的默认值
*/
public String defaultValue() default "";
/**
* 提示信息
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
public boolean needMerge() default false;
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
public boolean isExport() default true;
/**
* 另一个类中的属性名称,支持多级获取,以小数点隔开
*/
public String targetAttr() default "";
/**
* 是否自动统计数据,在最后追加一行统计数据总和
*/
public boolean isStatistics() default false;
/**
* 导出类型0数字 1字符串 2图片
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景颜色
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
/**
* 导出列头字体颜色
*/
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景颜色
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
/**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class;
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
/**
* 字段类型0导出导入1仅导出2仅导入
*/
Type type() default Type.ALL;
public enum Type {
/** 导出或导入 */
ALL(0),
/** 仅导出 */
EXPORT(1),
/** 仅导入 */
IMPORT(2);
private final int value;
Type(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
public enum ColumnType {
/** 数字 */
NUMERIC(0),
/** 字符串 */
STRING(1),
/** 图片 */
IMAGE(2),
/** 文本 */
TEXT(3);
private final int value;
ColumnType(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
}

View File

@ -0,0 +1,18 @@
package com.boyue.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel注解集
*
* @author boyue
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{
public Excel[] value();
}

View File

@ -0,0 +1,51 @@
package com.boyue.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.boyue.common.enums.BusinessType;
import com.boyue.common.enums.OperatorType;
/**
* 自定义操作日志记录注解
*
* @author boyue
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}

View File

@ -0,0 +1,41 @@
package com.boyue.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.boyue.common.constant.CacheConstants;
import com.boyue.common.enums.LimitType;
/**
* 限流注解
*
* @author boyue
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
/**
* 限流key
*/
public String key() default CacheConstants.RATE_LIMIT_KEY;
/**
* 限流时间,单位秒
*/
public int time() default 60;
/**
* 限流次数
*/
public int count() default 100;
/**
* 限流类型
*/
public LimitType limitType() default LimitType.DEFAULT;
}

View File

@ -0,0 +1,31 @@
package com.boyue.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解防止表单重复提交
*
* @author boyue
*
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
/**
* 间隔时间(ms)小于此时间视为重复提交
*/
public int interval() default 5000;
/**
* 提示消息
*/
public String message() default "不允许重复提交,请稍候再试";
}

View File

@ -0,0 +1,25 @@
package com.boyue.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.boyue.common.config.serializer.SensitiveJsonSerializer;
import com.boyue.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author boyue
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

@ -0,0 +1,135 @@
package com.boyue.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author boyue
*/
@Component
@ConfigurationProperties(prefix = "boyue")
public class BoYueConfig
{
/** 项目名称 */
private String name;
/** 版本 */
private String version;
/** 版权年份 */
private String copyrightYear;
/** 上传路径 */
private static String profile;
private static String fileServer;
/** 获取地址开关 */
private static boolean addressEnabled;
/** 验证码类型 */
private static String captchaType;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getCopyrightYear()
{
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear)
{
this.copyrightYear = copyrightYear;
}
public static String getProfile()
{
return profile;
}
public void setProfile(String profile)
{
BoYueConfig.profile = profile;
}
public static boolean isAddressEnabled()
{
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled)
{
BoYueConfig.addressEnabled = addressEnabled;
}
public static String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
BoYueConfig.captchaType = captchaType;
}
public static String getFileServer() {
return fileServer;
}
public void setFileServer(String fileServer) {
BoYueConfig.fileServer = fileServer;
}
/**
* 获取导入上传路径
*/
public static String getImportPath()
{
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath()
{
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath()
{
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath()
{
return getProfile() + "/upload";
}
}

Some files were not shown because too many files have changed in this diff Show More