init
This commit is contained in:
commit
1c8238bbb7
|
@ -0,0 +1,33 @@
|
|||
services/HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
|
@ -0,0 +1,75 @@
|
|||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>common-generator</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
<!-- 数据库代码生成器 - 新版 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>3.5.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<configuration>
|
||||
<mainClass>MainKt</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,82 @@
|
|||
package cn.bunny.common.generator
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType
|
||||
import com.baomidou.mybatisplus.generator.FastAutoGenerator
|
||||
import com.baomidou.mybatisplus.generator.config.OutputFile
|
||||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy
|
||||
import org.apache.ibatis.annotations.Mapper
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 代码生成器
|
||||
*/
|
||||
class AdminGenerator {
|
||||
companion object {
|
||||
// 数据连接
|
||||
private const val SQL_HOST: String =
|
||||
"jdbc:mysql://192.168.3.98:3304/auth_admin?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true"
|
||||
|
||||
// 作者名称
|
||||
private const val AUTHOR: String = "Bunny"
|
||||
|
||||
// 公共路径
|
||||
private const val OUTPUT_DIR: String = "D:\\MyFolder\\auth-admin\\auth-server\\services"
|
||||
|
||||
// 实体类名称
|
||||
private const val ENTITY: String = "Bunny"
|
||||
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
generation("sys_user")
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表名生成相应结构代码
|
||||
*
|
||||
* @param tableName 表名
|
||||
*/
|
||||
fun generation(vararg tableName: String) {
|
||||
FastAutoGenerator.create(SQL_HOST, "root", "02120212")
|
||||
.globalConfig { builder ->
|
||||
// 添加作者名称
|
||||
builder.author(AUTHOR) // 启用swagger
|
||||
.enableSwagger() // 指定输出目录
|
||||
.outputDir("$OUTPUT_DIR/src/main/kotlin")
|
||||
}
|
||||
.packageConfig { builder ->
|
||||
builder.entity(ENTITY) // 实体类包名
|
||||
.parent("cn.bunny.services")
|
||||
.controller("controller") // 控制层包名
|
||||
.mapper("mapper") // mapper层包名
|
||||
.service("service") // service层包名
|
||||
.serviceImpl("service.impl") // service实现类包名
|
||||
// 自定义mapper.xml文件输出目录
|
||||
.pathInfo(Collections.singletonMap(OutputFile.xml, "$OUTPUT_DIR/src/main/resources/mapper"))
|
||||
}
|
||||
.strategyConfig { builder ->
|
||||
// 设置要生成的表名
|
||||
builder.addInclude(*tableName)
|
||||
.addTablePrefix("sys_")// 设置表前缀过滤
|
||||
.entityBuilder()
|
||||
.enableLombok()
|
||||
.enableChainModel()
|
||||
.naming(NamingStrategy.underline_to_camel) // 数据表映射实体命名策略:默认下划线转驼峰underline_to_camel
|
||||
.columnNaming(NamingStrategy.underline_to_camel) // 表字段映射实体属性命名规则:默认null,不指定按照naming执行
|
||||
.idType(IdType.AUTO) // 添加全局主键类型
|
||||
.formatFileName("%s") // 格式化实体名称,%s取消首字母I,
|
||||
.mapperBuilder()
|
||||
.mapperAnnotation(Mapper::class.java) // 开启mapper注解
|
||||
.enableBaseResultMap() // 启用xml文件中的BaseResultMap 生成
|
||||
.enableBaseColumnList() // 启用xml文件中的BaseColumnList
|
||||
.formatMapperFileName("%sMapper") // 格式化Dao类名称
|
||||
.formatXmlFileName("%sMapper") // 格式化xml文件名称
|
||||
.serviceBuilder()
|
||||
.formatServiceFileName("%sService") // 格式化 service 接口文件名称
|
||||
.formatServiceImplFileName("%sServiceImpl") // 格式化 service 接口文件名称
|
||||
.controllerBuilder()
|
||||
.enableRestStyle()
|
||||
}
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.bunny.common.generator
|
||||
|
||||
/**
|
||||
* 代码生成器
|
||||
*/
|
||||
class I18nGenerator {
|
||||
companion object {
|
||||
// 数据连接
|
||||
private const val SQL_HOST: String =
|
||||
"jdbc:mysql://106.15.251.123:3305/auth_i18n?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true"
|
||||
|
||||
// 作者名称
|
||||
private const val AUTHOR: String = "Bunny"
|
||||
|
||||
// 公共路径
|
||||
private const val OUTPUT_DIR: String = "D:\\MyFolder\\auth-admin\\auth-server\\services"
|
||||
|
||||
// 实体类名称
|
||||
private const val ENTITY: String = "Bunny"
|
||||
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
AdminGenerator.generation("i18n", "i18n_type")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>common-service</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- 解决 javax.xml.bind 错误 -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
<!-- httpclient -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
<!-- redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<!-- redisson 分布式锁-->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>3.26.1</version>
|
||||
</dependency>
|
||||
<!-- minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<!-- 查询ip地址 -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>2.6.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>process-sources</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<configuration>
|
||||
<mainClass>MainKt</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,40 @@
|
|||
package cn.bunny.common.service.config
|
||||
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation
|
||||
import io.swagger.v3.oas.models.OpenAPI
|
||||
import io.swagger.v3.oas.models.info.Contact
|
||||
import io.swagger.v3.oas.models.info.Info
|
||||
import io.swagger.v3.oas.models.info.License
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.springdoc.core.models.GroupedOpenApi
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
open class Knife4jConfig {
|
||||
@Bean
|
||||
open fun openAPI(): OpenAPI {
|
||||
// 作者等信息
|
||||
val contact = Contact().name("Bunny").email("1319900154@qq.com").url("https://kotlinlang.org/docs")
|
||||
// 使用协议
|
||||
val license = License().name("MIT").url("https://MUT.com")
|
||||
// 相关信息
|
||||
val info = Info().title("Bunny-Java-Template").description("Bunny的Java模板").version("v1.0.0").contact(contact)
|
||||
.license(license).termsOfService("维护不易~求个start")
|
||||
|
||||
return OpenAPI().info(info).externalDocs(ExternalDocumentation())
|
||||
}
|
||||
|
||||
// 前台相关分类接口
|
||||
@Bean
|
||||
open fun groupedOpenApi(): GroupedOpenApi {
|
||||
return GroupedOpenApi.builder().group("web前台接口管理").pathsToMatch("/api/**").build()
|
||||
}
|
||||
|
||||
// 管理员相关分类接口
|
||||
@Bean
|
||||
open fun groupedOpenAdminApi(): GroupedOpenApi {
|
||||
return GroupedOpenApi.builder().group("admin管理员接口请求").pathsToMatch("/admin/**").build()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.bunny.common.service.config
|
||||
|
||||
import cn.bunny.common.service.context.BaseContext
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
|
||||
import org.apache.ibatis.reflection.MetaObject
|
||||
import org.springframework.stereotype.Component
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* 配置MP在修改和新增时的操作
|
||||
*/
|
||||
@Component
|
||||
class MyBatisPlusFieldConfig : MetaObjectHandler {
|
||||
/**
|
||||
* 使用mp做添加操作时候,这个方法执行
|
||||
*/
|
||||
override fun insertFill(metaObject: MetaObject) {
|
||||
this.strictInsertFill(metaObject, "isDeleted", Int::class.java, 0)
|
||||
|
||||
this.strictInsertFill(metaObject, "createTime", LocalDateTime::class.java, LocalDateTime.now())
|
||||
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::class.java, LocalDateTime.now())
|
||||
|
||||
this.strictInsertFill(metaObject, "createUser", Long::class.java, BaseContext.getUserId())
|
||||
this.strictInsertFill(metaObject, "updateUser", Long::class.java, BaseContext.getUserId())
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用mp做修改操作时候,这个方法执行
|
||||
*/
|
||||
override fun updateFill(metaObject: MetaObject) {
|
||||
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::class.java, LocalDateTime.now())
|
||||
this.strictUpdateFill(metaObject, "updateUser", Long::class.java, BaseContext.getUserId())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.bunny.common.service.config
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement
|
||||
|
||||
/**
|
||||
* Mybatis-Plus配置类
|
||||
*/
|
||||
@EnableTransactionManagement
|
||||
@Configuration
|
||||
@Slf4j
|
||||
open class MybatisPlusConfig {
|
||||
@Bean
|
||||
open fun mybatisPlusInterceptor(): MybatisPlusInterceptor {
|
||||
val interceptor = MybatisPlusInterceptor()
|
||||
// 分页插件
|
||||
val paginationInnerInterceptor = PaginationInnerInterceptor(DbType.MYSQL)
|
||||
paginationInnerInterceptor.maxLimit = 100L // ? 设置最大分页为100
|
||||
interceptor.addInnerInterceptor(paginationInnerInterceptor)
|
||||
// 乐观锁
|
||||
interceptor.addInnerInterceptor(OptimisticLockerInnerInterceptor())
|
||||
// 防止全表删除
|
||||
interceptor.addInnerInterceptor(BlockAttackInnerInterceptor())
|
||||
|
||||
return interceptor
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package cn.bunny.common.service.config
|
||||
|
||||
import cn.bunny.dao.pojo.constant.LocalDateTimeConstant
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.springframework.cache.CacheManager
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Primary
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
import org.springframework.data.redis.cache.RedisCacheManager
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
|
||||
import org.springframework.data.redis.core.RedisTemplate
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer
|
||||
import org.springframework.stereotype.Component
|
||||
import java.time.Duration
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/**
|
||||
* 设置Redis序列化
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
class RedisConfiguration {
|
||||
/**
|
||||
* 使用StringRedisSerializer序列化为字符串
|
||||
*/
|
||||
@Bean
|
||||
fun redisTemplate(connectionFactory: LettuceConnectionFactory?): RedisTemplate<Any, Any> {
|
||||
val redisTemplate = RedisTemplate<Any, Any>()
|
||||
redisTemplate.connectionFactory = connectionFactory
|
||||
// 设置key序列化为string
|
||||
redisTemplate.keySerializer = StringRedisSerializer()
|
||||
// 设置value序列化为JSON,使用GenericJackson2JsonRedisSerializer替换默认序列化
|
||||
redisTemplate.valueSerializer = GenericJackson2JsonRedisSerializer()
|
||||
redisTemplate.hashKeySerializer = StringRedisSerializer()
|
||||
redisTemplate.hashValueSerializer = GenericJackson2JsonRedisSerializer()
|
||||
|
||||
return redisTemplate
|
||||
}
|
||||
|
||||
/**
|
||||
* * 配置Redis过期时间30天
|
||||
* 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
fun cacheManagerWithMouth(factory: RedisConnectionFactory?): CacheManager {
|
||||
// 配置序列化
|
||||
val config = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
||||
.entryTtl(Duration.ofDays(30))
|
||||
|
||||
return RedisCacheManager.builder(factory!!).cacheDefaults(config).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* * 配置redis过期时间3分钟
|
||||
*
|
||||
* @param factory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
fun cacheManagerWithMinutes(factory: RedisConnectionFactory?): CacheManager {
|
||||
// 配置序列化
|
||||
val config = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
||||
.entryTtl(Duration.ofMinutes(3))
|
||||
|
||||
return RedisCacheManager.builder(factory!!).cacheDefaults(config).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* * 配置Redis过期时间1小时
|
||||
*
|
||||
* @param factory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
fun cacheManagerWithHours(factory: RedisConnectionFactory?): CacheManager {
|
||||
// 配置序列化
|
||||
val config = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
||||
.entryTtl(Duration.ofHours(1))
|
||||
|
||||
return RedisCacheManager.builder(factory!!).cacheDefaults(config).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定的日期模式
|
||||
*/
|
||||
private fun jsonRedisSerializer(): Jackson2JsonRedisSerializer<Any> {
|
||||
val mapper = ObjectMapper()
|
||||
// 设置ObjectMapper访问权限
|
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
|
||||
// 记录序列化之后的数据类型,方便反序列化
|
||||
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL)
|
||||
// LocalDatetime序列化,默认不兼容jdk8日期序列化
|
||||
val timeModule = JavaTimeModule()
|
||||
timeModule.addDeserializer(
|
||||
LocalDate::class.java,
|
||||
LocalDateDeserializer(DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD))
|
||||
)
|
||||
timeModule.addSerializer(
|
||||
LocalDate::class.java,
|
||||
LocalDateSerializer(DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD))
|
||||
)
|
||||
|
||||
timeModule.addDeserializer(
|
||||
LocalDateTime::class.java,
|
||||
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS))
|
||||
)
|
||||
timeModule.addSerializer(
|
||||
LocalDateTime::class.java,
|
||||
LocalDateTimeSerializer(DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS))
|
||||
)
|
||||
// 关闭默认的日期格式化方式,默认UTC日期格式 yyyy-MM-dd’T’HH:mm:ss.SSS
|
||||
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||
mapper.registerModule(timeModule)
|
||||
|
||||
return Jackson2JsonRedisSerializer(mapper, Any::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package cn.bunny.common.service.config
|
||||
|
||||
import cn.bunny.common.service.interceptor.UserTokenInterceptor
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
open class WebMvcConfiguration : WebMvcConfigurer {
|
||||
@Autowired
|
||||
private val userTokenInterceptor: UserTokenInterceptor? = null
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*
|
||||
* @param registry 跨域注册表
|
||||
*/
|
||||
override fun addCorsMappings(registry: CorsRegistry) {
|
||||
registry.addMapping("/**") // 是否发送Cookies
|
||||
.allowCredentials(true) // 放行哪些原始域
|
||||
.allowedOriginPatterns("*").allowedMethods("GET", "POST", "PUT", "DELETE").allowedHeaders("*")
|
||||
.exposedHeaders("*")
|
||||
}
|
||||
|
||||
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||
// TODO Spring Security 和这个拦截器任选一个
|
||||
val excludeList = arrayOf(
|
||||
"/", "/test/**", "/*.html", "/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico",
|
||||
"/swagger-resources/**", "/swagger-ui.html/**", "/admin/login", "/v3/**", "/api/**"
|
||||
)
|
||||
registry.addInterceptor(userTokenInterceptor!!).excludePathPatterns(*excludeList)
|
||||
|
||||
// TODO 如果想使用普通JWT可以使用这个,不使用 SpringSecurity6
|
||||
// registry.addInterceptor(userTokenInterceptor).addPathPatterns("/api/**").excludePathPatterns(excludeList);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package cn.bunny.common.service.context
|
||||
|
||||
import cn.bunny.dao.vo.user.LoginVo
|
||||
|
||||
open class BaseContext {
|
||||
companion object {
|
||||
private val userId = ThreadLocal<Long>()
|
||||
private val username = ThreadLocal<String>()
|
||||
private val loginVo = ThreadLocal<LoginVo>()
|
||||
|
||||
// 用户id相关
|
||||
@JvmStatic
|
||||
fun getUserId(): Long {
|
||||
return userId.get()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setUserId(id: Long) {
|
||||
userId.set(id)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getUsername(): String {
|
||||
return username.get()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setUsername(username: String) {
|
||||
this.username.set(username)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getLoginVo(): LoginVo {
|
||||
return loginVo.get()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setLoginVo(loginVo: LoginVo) {
|
||||
this.loginVo.set(loginVo)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removeUser() {
|
||||
username.remove()
|
||||
userId.remove()
|
||||
loginVo.remove()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package cn.bunny.common.service.exception
|
||||
|
||||
import cn.bunny.dao.pojo.result.ResultCodeEnum
|
||||
import lombok.Getter
|
||||
import lombok.ToString
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@Slf4j
|
||||
class BunnyException : RuntimeException {
|
||||
// 状态码
|
||||
var code: Int? = null
|
||||
|
||||
// 描述信息
|
||||
override var message: String
|
||||
|
||||
constructor(code: Int?, message: String) : super(message) {
|
||||
this.code = code
|
||||
this.message = message
|
||||
}
|
||||
|
||||
constructor(message: String) : super(message) {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
constructor(codeEnum: ResultCodeEnum) : super(codeEnum.message) {
|
||||
this.code = codeEnum.code
|
||||
this.message = codeEnum.message
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package cn.bunny.common.service.exception
|
||||
|
||||
import cn.bunny.dao.pojo.constant.ExceptionConstant
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import cn.bunny.dao.pojo.result.ResultCodeEnum
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.ResponseBody
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.AccessDeniedException
|
||||
import java.sql.SQLIntegrityConstraintViolationException
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
class GlobalExceptionHandler {
|
||||
private val logger = LogManager.getLogger(GlobalExceptionHandler::class.java)
|
||||
|
||||
// 自定义异常信息
|
||||
@ExceptionHandler(BunnyException::class)
|
||||
@ResponseBody
|
||||
fun exceptionHandler(exception: BunnyException): Result<Any?> {
|
||||
logger.error("GlobalExceptionHandler===>自定义异常信息:{}", exception.message)
|
||||
|
||||
val code = if (exception.code != null) exception.code else 500
|
||||
return Result.error(null, code, exception.message)
|
||||
}
|
||||
|
||||
// 运行时异常信息
|
||||
@ExceptionHandler(RuntimeException::class)
|
||||
@ResponseBody
|
||||
@Throws(FileNotFoundException::class)
|
||||
fun exceptionHandler(exception: RuntimeException): Result<Any?> {
|
||||
logger.error("GlobalExceptionHandler===>运行时异常信息:{}", exception.message)
|
||||
exception.printStackTrace()
|
||||
return Result.error(null, 500, "出错了啦")
|
||||
}
|
||||
|
||||
// 捕获系统异常
|
||||
@ExceptionHandler(Exception::class)
|
||||
@ResponseBody
|
||||
fun error(exception: Exception): Result<Any?> {
|
||||
logger.error("GlobalExceptionHandler===>系统异常信息:{}", exception.message)
|
||||
|
||||
// 错误消息
|
||||
val message = exception.message ?: ""
|
||||
// 匹配到内容
|
||||
val patternString = "Request method '(\\w+)' is not supported"
|
||||
val matcher = Pattern.compile(patternString).matcher(message)
|
||||
|
||||
return when {
|
||||
matcher.find() -> Result.error(null, 500, "请求方法错误,不是 " + matcher.group(1))
|
||||
else -> Result.error(null, 500, "系统异常")
|
||||
}
|
||||
}
|
||||
|
||||
// 特定异常处理
|
||||
@ExceptionHandler(ArithmeticException::class)
|
||||
@ResponseBody
|
||||
fun error(exception: ArithmeticException): Result<Any?> {
|
||||
logger.error("GlobalExceptionHandler===>特定异常信息:{}", exception.message)
|
||||
|
||||
return Result.error(null, 500, exception.message)
|
||||
}
|
||||
|
||||
// spring security异常
|
||||
@ExceptionHandler(AccessDeniedException::class)
|
||||
@ResponseBody
|
||||
@Throws(
|
||||
AccessDeniedException::class
|
||||
)
|
||||
fun error(exception: AccessDeniedException): Result<String?> {
|
||||
logger.error("GlobalExceptionHandler===>spring security异常:{}", exception.message)
|
||||
|
||||
return Result.error(ResultCodeEnum.SERVICE_ERROR)
|
||||
}
|
||||
|
||||
// 处理SQL异常
|
||||
@ExceptionHandler(SQLIntegrityConstraintViolationException::class)
|
||||
@ResponseBody
|
||||
fun exceptionHandler(exception: SQLIntegrityConstraintViolationException): Result<String> {
|
||||
logger.error("GlobalExceptionHandler===>处理SQL异常:{}", exception.message)
|
||||
|
||||
val message = exception.message
|
||||
if (message!!.contains("Duplicate entry")) {
|
||||
// 截取用户名
|
||||
val username = message.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[2]
|
||||
// 错误信息
|
||||
val errorMessage = username + ExceptionConstant.ALREADY_USER_EXCEPTION
|
||||
return Result.error(errorMessage)
|
||||
} else {
|
||||
return Result.error(ExceptionConstant.UNKNOWN_EXCEPTION)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cn.bunny.common.service.utils
|
||||
|
||||
import cn.bunny.common.service.exception.BunnyException
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
@Slf4j
|
||||
object EmptyUtil {
|
||||
private val logger = LogManager.getLogger(EmptyUtil::class.java)
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
*
|
||||
* @param value 判断值
|
||||
* @param message 错误消息
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isEmpty(value: Any?, message: String?) {
|
||||
if (value == null || !StringUtils.hasText(value.toString())) {
|
||||
logger.error("为空对象错误:{},{}", value, message)
|
||||
throw BunnyException(message!!)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package cn.bunny.common.service.utils
|
||||
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
/**
|
||||
* * 计算文件大小
|
||||
*/
|
||||
@Component
|
||||
class FileUtil {
|
||||
companion object {
|
||||
/**
|
||||
* * 获取文件大小字符串
|
||||
*/
|
||||
fun getSize(fileSize: Long): String {
|
||||
val fileSizeInKB = fileSize / 1024.00
|
||||
val fileSizeInMB = fileSizeInKB / 1024
|
||||
val fileSizeInGB = fileSizeInMB / 1024
|
||||
|
||||
return when {
|
||||
fileSizeInGB >= 1 -> "${String.format("%.2f", fileSizeInGB).toDouble()}GB"
|
||||
fileSizeInMB >= 1 -> "${String.format("%.2f", fileSizeInMB).toDouble()}MB"
|
||||
fileSizeInKB >= 1 -> "${String.format("%.2f", fileSizeInKB).toDouble()}KB"
|
||||
else -> "${fileSize}B"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package cn.bunny.common.service.utils
|
||||
|
||||
import cn.bunny.common.service.exception.BunnyException
|
||||
import jakarta.annotation.PostConstruct
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.lionsoul.ip2region.xdb.Searcher
|
||||
import org.springframework.core.io.ClassPathResource
|
||||
import org.springframework.util.FileCopyUtils
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@Slf4j
|
||||
class IpUtil {
|
||||
|
||||
companion object {
|
||||
private val logger = LogManager.getLogger(IpUtil::class.java)
|
||||
private var searcher: Searcher? = null
|
||||
|
||||
/**
|
||||
* 判断是否为合法 IP
|
||||
*/
|
||||
private fun checkIp(ipAddress: String): Boolean {
|
||||
val ip = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"
|
||||
val pattern = Pattern.compile(ip)
|
||||
val matcher = pattern.matcher(ipAddress)
|
||||
|
||||
return matcher.matches()
|
||||
}
|
||||
|
||||
/**
|
||||
* 在服务启动时,将 ip2region 加载到内存中
|
||||
*/
|
||||
@PostConstruct
|
||||
private fun initIp2Region() {
|
||||
try {
|
||||
val inputStream = ClassPathResource("/ipdb/ip2region.xdb").inputStream
|
||||
val bytes = FileCopyUtils.copyToByteArray(inputStream)
|
||||
searcher = Searcher.newWithBuffer(bytes)
|
||||
} catch (exception: Exception) {
|
||||
logger.error(exception.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 ip 所属地址
|
||||
*
|
||||
* @param checkIp ip
|
||||
*/
|
||||
fun getIpRegion(checkIp: String): String {
|
||||
val ip = if (checkIp == "0:0:0:0:0:0:0:1") "127.0.0.1" else checkIp
|
||||
if (!checkIp(ip)) throw BunnyException("非法的IP地址")
|
||||
|
||||
val searchIpInfo = searcher?.search(ip)
|
||||
return searchIpInfo?.let { it ->
|
||||
val splitIpInfo = it.split("\\|".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (splitIpInfo.isNotEmpty()) {
|
||||
when {
|
||||
splitIpInfo[0] == "中国" -> splitIpInfo[2] // 国内属地返回省份
|
||||
splitIpInfo[0] == "0" && splitIpInfo[4] == "内网IP" -> splitIpInfo[4] // 内网 IP
|
||||
else -> splitIpInfo[0] // 国外属地返回国家
|
||||
}
|
||||
} else ""
|
||||
} ?: ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
package cn.bunny.common.service.utils
|
||||
|
||||
import io.jsonwebtoken.CompressionCodecs
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.micrometer.common.lang.Nullable
|
||||
import org.springframework.util.StringUtils
|
||||
import java.util.*
|
||||
|
||||
object JwtHelper {
|
||||
// 时间 按天 计算
|
||||
private const val TOKEN_EXPIRATION = (24 * 60 * 60 * 1000).toLong()
|
||||
|
||||
// JWT 的 秘钥
|
||||
private const val TOKEN_SIGN_KEY = "Bunny-Java-Template"
|
||||
|
||||
// 默认主题
|
||||
private const val SUBJECT = "Bunny"
|
||||
|
||||
// 默认时间
|
||||
private val time = Date(System.currentTimeMillis() + TOKEN_EXPIRATION * 7)
|
||||
|
||||
/**
|
||||
* 使用默认主题,默认时间,默认秘钥,创建自定义集合token
|
||||
*
|
||||
* @param map 集合
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(SUBJECT)
|
||||
.setExpiration(time)
|
||||
.signWith(SignatureAlgorithm.HS256, TOKEN_SIGN_KEY)
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.compressWith(CompressionCodecs.GZIP).compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认主题,默认秘钥,自定义时间,创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param time 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, time: Date?): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(SUBJECT)
|
||||
.signWith(SignatureAlgorithm.HS256, TOKEN_SIGN_KEY)
|
||||
.setExpiration(time)
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.compressWith(CompressionCodecs.GZIP).compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认主题,默认秘钥,自定义时间,创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param day 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, day: Int): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(SUBJECT)
|
||||
.signWith(SignatureAlgorithm.HS256, TOKEN_SIGN_KEY)
|
||||
.setExpiration(Date(System.currentTimeMillis() + TOKEN_EXPIRATION * day))
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.compressWith(CompressionCodecs.GZIP).compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认主题,默认秘钥,自定义key,创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param tokenSignKey 自定义key
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, tokenSignKey: String?): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(SUBJECT)
|
||||
.setExpiration(time)
|
||||
.signWith(SignatureAlgorithm.HS256, tokenSignKey)
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.compressWith(CompressionCodecs.GZIP).compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义主题,自定义时间,创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param subject 主题
|
||||
* @param time 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, subject: String?, time: Date?): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(subject)
|
||||
.setExpiration(time)
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.signWith(SignatureAlgorithm.HS256, TOKEN_SIGN_KEY)
|
||||
.compressWith(CompressionCodecs.GZIP)
|
||||
.compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param subject 主题
|
||||
* @param tokenSignKey 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, subject: String?, tokenSignKey: String?): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(subject)
|
||||
.setExpiration(time)
|
||||
.signWith(SignatureAlgorithm.HS256, tokenSignKey)
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.compressWith(CompressionCodecs.GZIP).compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param tokenSignKey 主题
|
||||
* @param time 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, tokenSignKey: String?, time: Int): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(SUBJECT)
|
||||
.setExpiration(Date(System.currentTimeMillis() + TOKEN_EXPIRATION * time))
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.signWith(SignatureAlgorithm.HS256, tokenSignKey)
|
||||
.compressWith(CompressionCodecs.GZIP)
|
||||
.compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param subject 主题
|
||||
* @param day 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, subject: String?, tokenSignKey: String?, day: Int): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(subject)
|
||||
.setExpiration(Date(System.currentTimeMillis() + TOKEN_EXPIRATION * day))
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.signWith(SignatureAlgorithm.HS256, tokenSignKey)
|
||||
.compressWith(CompressionCodecs.GZIP)
|
||||
.compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建集合形式token
|
||||
*
|
||||
* @param map 集合
|
||||
* @param subject 主题
|
||||
* @param time 过期时间
|
||||
* @return token
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createTokenWithMap(map: Map<String?, Any?>?, subject: String?, tokenSignKey: String?, time: Date?): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(subject)
|
||||
.setExpiration(time)
|
||||
.setClaims(map)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.signWith(SignatureAlgorithm.HS256, tokenSignKey)
|
||||
.compressWith(CompressionCodecs.GZIP)
|
||||
.compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户名和ID创建token
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param userName 用户名
|
||||
* @param day 过期时间
|
||||
* @return token值
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createToken(userId: Long?, userName: String?, day: Int): String {
|
||||
return Jwts.builder()
|
||||
.setSubject(SUBJECT)
|
||||
.setExpiration(Date(System.currentTimeMillis() + TOKEN_EXPIRATION * day))
|
||||
.claim("userId", userId)
|
||||
.claim("userName", userName)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.signWith(SignatureAlgorithm.HS256, TOKEN_SIGN_KEY)
|
||||
.compressWith(CompressionCodecs.GZIP)
|
||||
.compact()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用token获取map集合,使用默认秘钥
|
||||
*
|
||||
* @param token token
|
||||
* @return map集合
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getMapByToken(token: String?): Map<String, Any>? {
|
||||
try {
|
||||
if (!StringUtils.hasText(token)) return null
|
||||
val claims = Jwts.parser().setSigningKey(TOKEN_SIGN_KEY).parseClaimsJws(token).body
|
||||
|
||||
// 将 body 值转为map
|
||||
return HashMap(claims)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用token获取map集合
|
||||
*
|
||||
* @param token token
|
||||
* @param signKey 秘钥
|
||||
* @return map集合
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getMapByToken(token: String?, signKey: String?): Map<String, Any>? {
|
||||
try {
|
||||
if (!StringUtils.hasText(token)) return null
|
||||
val claimsJws = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token)
|
||||
val body = claimsJws.body
|
||||
// 将 body 值转为map
|
||||
return HashMap(body)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据token获取主题
|
||||
*
|
||||
* @param token token
|
||||
* @return 主题
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getSubjectByToken(token: String): String? {
|
||||
return getSubjectByTokenHandler(token, TOKEN_SIGN_KEY)
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@JvmStatic
|
||||
private fun getSubjectByTokenHandler(token: String, tokenSignKey: String): String? {
|
||||
try {
|
||||
if (!StringUtils.hasText(token)) return null
|
||||
val claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token)
|
||||
val body = claimsJws.body
|
||||
|
||||
return body.subject
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据token获取主题
|
||||
*
|
||||
* @param token token
|
||||
* @return 主题
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getSubjectByToken(token: String, tokenSignKey: String): String? {
|
||||
return getSubjectByTokenHandler(token, tokenSignKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据token获取用户ID
|
||||
*
|
||||
* @param token token
|
||||
* @return 用户ID
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getUserId(token: String?): Long? {
|
||||
try {
|
||||
if (!StringUtils.hasText(token)) return null
|
||||
|
||||
val claimsJws = Jwts.parser().setSigningKey(TOKEN_SIGN_KEY).parseClaimsJws(token)
|
||||
val claims = claimsJws.body
|
||||
|
||||
return claims["userId"].toString().toLong()
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据token获取用户名
|
||||
*
|
||||
* @param token token
|
||||
* @return 用户名
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getUsername(token: String?): String? {
|
||||
try {
|
||||
if (!StringUtils.hasText(token)) return ""
|
||||
|
||||
val claimsJws = Jwts.parser().setSigningKey(TOKEN_SIGN_KEY).parseClaimsJws(token)
|
||||
val claims = claimsJws.body
|
||||
return claims["userName"] as String?
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断token是否过期
|
||||
*
|
||||
* @param token token
|
||||
* @return 是否过期
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isExpired(token: String): Boolean {
|
||||
return isExpiredUtil(token, TOKEN_SIGN_KEY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断token是否过期
|
||||
*
|
||||
* @param token token
|
||||
* @return 是否过期
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isExpired(token: String, tokenSignKey: String): Boolean {
|
||||
return isExpiredUtil(token, tokenSignKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否过期
|
||||
*
|
||||
* @param token token
|
||||
* @param tokenSignKey key值
|
||||
* @return 是否过期
|
||||
*/
|
||||
@JvmStatic
|
||||
private fun isExpiredUtil(token: String, tokenSignKey: String): Boolean {
|
||||
try {
|
||||
val claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token)
|
||||
val expiration = claimsJws.body.expiration
|
||||
|
||||
return expiration != null && expiration.before(Date())
|
||||
} catch (exception: Exception) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package cn.bunny.common.service.utils
|
||||
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import cn.bunny.dao.pojo.result.ResultCodeEnum
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
|
||||
class ResponseHandlerUtil {
|
||||
companion object {
|
||||
fun loginAuthHandler(response: HttpServletResponse, loginAuth: ResultCodeEnum): Boolean {
|
||||
ResponseUtil.out(response, Result.error(loginAuth))
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package cn.bunny.common.service.utils
|
||||
|
||||
import cn.bunny.dao.pojo.email.EmailSend
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.http.HttpStatus
|
||||
import java.io.IOException
|
||||
|
||||
open class ResponseUtil {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun out(response: HttpServletResponse, result: Result<Any?>?) {
|
||||
try {
|
||||
val mapper = ObjectMapper()
|
||||
// 注册JavaTimeModule模块
|
||||
mapper.registerModule(JavaTimeModule())
|
||||
response.contentType = "application/json;charset=UTF-8"
|
||||
response.status = HttpStatus.OK.value()
|
||||
mapper.writeValue(response.writer, result)
|
||||
val emailSend = EmailSend()
|
||||
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.bunny.common.service.utils.email
|
||||
|
||||
import cn.bunny.common.service.utils.EmptyUtil.isEmpty
|
||||
import cn.bunny.dao.pojo.constant.MailMessageConstant
|
||||
import cn.bunny.dao.pojo.email.EmailSend
|
||||
|
||||
class MailSendCheckUtil {
|
||||
companion object {
|
||||
/**
|
||||
* 检测发送对象是否为空的对象
|
||||
*
|
||||
* @param emailSend 邮件发送对象
|
||||
*/
|
||||
@JvmStatic
|
||||
fun check(emailSend: EmailSend) {
|
||||
// 空发送对象
|
||||
isEmpty(emailSend, MailMessageConstant.EMPTY_SEND_OBJECT)
|
||||
// 收件人不能为空
|
||||
isEmpty(emailSend.sendTo, MailMessageConstant.ADDRESS_NOT_NULL)
|
||||
// 标题不能为空
|
||||
isEmpty(emailSend.subject, MailMessageConstant.TITLE_NOT_NULL)
|
||||
// 发送消息不能为空
|
||||
isEmpty(emailSend.message, MailMessageConstant.SEND_MESSAGE_NOT_NULL)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package cn.bunny.common.service.utils.email
|
||||
|
||||
import cn.bunny.common.service.utils.email.MailSendCheckUtil.Companion.check
|
||||
import cn.bunny.dao.pojo.email.EmailSend
|
||||
import cn.bunny.dao.pojo.email.EmailSendInit
|
||||
import jakarta.mail.MessagingException
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl
|
||||
import org.springframework.mail.javamail.MimeMessageHelper
|
||||
import java.util.*
|
||||
|
||||
class MailSenderUtil(emailSendInit: EmailSendInit) {
|
||||
private val username: String?
|
||||
private val javaMailSender: JavaMailSenderImpl
|
||||
|
||||
/**
|
||||
* 初始化构造函数进行当前类赋值
|
||||
*/
|
||||
init {
|
||||
val javaMailSender = JavaMailSenderImpl()
|
||||
javaMailSender.host = emailSendInit.host
|
||||
javaMailSender.port = emailSendInit.port!!
|
||||
javaMailSender.username = emailSendInit.username
|
||||
javaMailSender.password = emailSendInit.password
|
||||
javaMailSender.protocol = "smtps"
|
||||
javaMailSender.defaultEncoding = "UTF-8"
|
||||
|
||||
this.username = emailSendInit.username
|
||||
this.javaMailSender = javaMailSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 综合邮箱发送
|
||||
*
|
||||
* @param emailSend 邮件消息
|
||||
*/
|
||||
@Throws(MessagingException::class)
|
||||
fun sendEmail(emailSend: EmailSend) {
|
||||
check(emailSend)
|
||||
|
||||
// 创建 MimeMessage 对象用于发送邮件富文本或者附件
|
||||
val message = javaMailSender.createMimeMessage()
|
||||
// 创建 MimeMessageHelper
|
||||
val helper = MimeMessageHelper(message, true)
|
||||
|
||||
// 设置发送人
|
||||
helper.setFrom(username!!)
|
||||
// 设置邮件接受者
|
||||
helper.setTo(emailSend.sendTo!!)
|
||||
// 设置邮件主题
|
||||
helper.setSubject(emailSend.subject!!)
|
||||
// 设置发送消息 为富文本
|
||||
helper.setText(emailSend.message!!, emailSend.isRichText!!)
|
||||
// 设置抄送人
|
||||
helper.setCc(emailSend.ccParam!!.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
|
||||
// 邮件添加附件
|
||||
val files = emailSend.file
|
||||
for (file in files!!) {
|
||||
helper.addAttachment(Objects.requireNonNull(file.originalFilename), file)
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
javaMailSender.send(message)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
mail:
|
||||
host: smtp.qq.com # 邮箱地址
|
||||
port: 465 # 邮箱端口号
|
||||
username: xxx@qq.com # 设置发送邮箱
|
||||
password: xx # 如果是纯数字要加引号
|
||||
default-encoding: UTF-8 # 设置编码格式
|
||||
protocol: smtps
|
||||
properties:
|
||||
mail:
|
||||
debug: true # 是否开启debug模式发送邮件
|
||||
smtp:
|
||||
auth: true
|
||||
connectionTimeout: 5000 # 设置连接延迟
|
||||
timeout: 5000 # 延迟时间
|
||||
writeTimeout: 5000 # 写入邮箱延迟
|
||||
allow8BitMime: true
|
||||
sendPartial: true
|
||||
ssl:
|
||||
enabled: true # 是否开启SSL连接
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory # 必要设置!!!
|
|
@ -0,0 +1,26 @@
|
|||
package cn.bunny.common.service.utils.minio
|
||||
|
||||
import io.minio.MinioClient
|
||||
import lombok.Data
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "bunny.minio")
|
||||
@ConditionalOnProperty(name = ["bunny.minio.bucket-name"]) // 当属性有值时这个配置才生效
|
||||
@Data
|
||||
@Slf4j
|
||||
open class MinioProperties {
|
||||
val endpointUrl: String? = null
|
||||
val accessKey: String? = null
|
||||
val secretKey: String? = null
|
||||
val bucketName: String? = null
|
||||
|
||||
@Bean
|
||||
open fun minioClient(): MinioClient {
|
||||
return MinioClient.builder().endpoint(endpointUrl).credentials(accessKey, secretKey).build()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package cn.bunny.common.service.utils.minio;
|
||||
|
||||
import cn.bunny.common.service.exception.BunnyException;
|
||||
import cn.bunny.dao.pojo.constant.FileMessageConstant;
|
||||
import cn.bunny.dao.pojo.constant.MinioConstant;
|
||||
import cn.bunny.dao.pojo.file.MinioFIlePath;
|
||||
import io.minio.GetObjectArgs;
|
||||
import io.minio.GetObjectResponse;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.PutObjectArgs;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Minio操作工具类 简化操作步骤
|
||||
* By:Bunny0212
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MinioUtil {
|
||||
@Autowired
|
||||
private MinioProperties properties;
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
|
||||
/**
|
||||
* 获取Minio文件路径
|
||||
*/
|
||||
public static MinioFIlePath getMinioFilePath(String buckName, String minioPreType, MultipartFile file) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
// 定义日期时间格式
|
||||
LocalDateTime currentDateTime = LocalDateTime.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM-dd");
|
||||
String extension = "";
|
||||
|
||||
// 原始文件名
|
||||
String filename = file.getOriginalFilename();
|
||||
if (StringUtils.hasText(filename) && filename.contains(".")) {
|
||||
extension = "." + filename.substring(filename.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
// UUID防止重名
|
||||
String uuidFilename = uuid + extension;
|
||||
|
||||
// 拼接时间+UUID文件名
|
||||
String timeUuidFilename = currentDateTime.format(formatter) + "/" + uuidFilename;// 加上时间路径
|
||||
|
||||
// 上传根文件夹+拼接时间+UUID文件名
|
||||
String filepath = MinioConstant.getType(minioPreType) + timeUuidFilename;
|
||||
|
||||
// 桶名称+上传根文件夹+拼接时间+UUID文件名
|
||||
String buckNameFilepath = "/" + buckName + MinioConstant.getType(minioPreType) + timeUuidFilename;
|
||||
|
||||
// 设置及Minio基础信息
|
||||
MinioFIlePath minioFIlePath = new MinioFIlePath();
|
||||
minioFIlePath.setFilename(filename);
|
||||
minioFIlePath.setUuidFilename(uuidFilename);
|
||||
minioFIlePath.setTimeUuidFilename(timeUuidFilename);
|
||||
minioFIlePath.setFilepath(filepath);
|
||||
minioFIlePath.setBucketNameFilepath(buckNameFilepath);
|
||||
|
||||
return minioFIlePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 上传文件并返回处理信息
|
||||
*/
|
||||
public MinioFIlePath getUploadMinioObjectFilePath(MultipartFile file, String minioPreType) throws IOException {
|
||||
// 如果buckName为空,设置为默认的桶
|
||||
String bucketName = properties.getBucketName();
|
||||
if (file != null) {
|
||||
MinioFIlePath minioFile = getMinioFilePath(bucketName, minioPreType, file);
|
||||
String filepath = minioFile.getFilepath();
|
||||
|
||||
// 上传对象
|
||||
putObject(bucketName, filepath, file.getInputStream(), file.getSize());
|
||||
|
||||
// 设置图片地址
|
||||
return minioFile;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认bucket文件,并返回字节数组
|
||||
*
|
||||
* @param objectName 对象名称
|
||||
* @return 文件流对象
|
||||
*/
|
||||
public byte[] getBucketObjectByte(String objectName) {
|
||||
// 如果buckName为空,设置为默认的桶
|
||||
String bucketName = properties.getBucketName();
|
||||
|
||||
try {
|
||||
objectName = objectName.replace("/" + bucketName, "");
|
||||
GetObjectResponse getObjectResponse = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
|
||||
|
||||
return getObjectResponse.readAllBytes();
|
||||
} catch (Exception exception) {
|
||||
exception.getStackTrace();
|
||||
}
|
||||
throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Minio全路径名,Object带有桶名称
|
||||
*
|
||||
* @param objectName 对象名称
|
||||
* @return 全路径
|
||||
*/
|
||||
public String getObjectNameFullPath(String objectName) {
|
||||
String url = properties.getEndpointUrl();
|
||||
|
||||
return url + objectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param filename 文件名
|
||||
* @param inputStream 输入流
|
||||
* @param size 大小
|
||||
*/
|
||||
public void putObject(String bucketName, String filename, InputStream inputStream, Long size) {
|
||||
try {
|
||||
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(inputStream, size, -1).build());
|
||||
} catch (Exception exception) {
|
||||
exception.getStackTrace();
|
||||
throw new BunnyException(FileMessageConstant.FILE_UPLOAD_EXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>kotlin-single</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>common</name>
|
||||
<url>https://maven.apache.org</url>
|
||||
<modules>
|
||||
<module>common-generator</module>
|
||||
<module>common-service</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
<kotlin.compiler.jvmTarget>17</kotlin.compiler.jvmTarget>
|
||||
<java.version>17</java.version>
|
||||
<kotlin.version>2.0.20</kotlin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- dao 层 -->
|
||||
<dependency>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>dao</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
</dependency>
|
||||
<!-- hutool -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<!-- mysql连接驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!-- mysql连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>kotlin-single</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dao</artifactId>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
<kotlin.compiler.jvmTarget>17</kotlin.compiler.jvmTarget>
|
||||
<java.version>17</java.version>
|
||||
<kotlin.version>2.0.20</kotlin.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mavenCentral</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<!-- mybatis-plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- knife4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
<!-- 实体类注解 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.6.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit5</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>process-sources</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<configuration>
|
||||
<mainClass>MainKt</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,25 @@
|
|||
package cn.bunny.dao.dto.email
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import jakarta.validation.constraints.NotBlank
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Schema(name = "EmailTemplateDto", title = "邮箱模板请求内容", description = "邮箱模板请求内容")
|
||||
class EmailTemplateDto {
|
||||
@Schema(name = "templateName", title = "模板名称")
|
||||
var templateName: @NotBlank(message = "模板名称不能为空") String? = null
|
||||
|
||||
@Schema(name = "subject", title = "主题")
|
||||
var subject: @NotBlank(message = "主题不能为空") String? = null
|
||||
|
||||
@Schema(name = "body", title = "邮件内容")
|
||||
var body: @NotBlank(message = "邮件内容不能为空") String? = null
|
||||
|
||||
@Schema(name = "type", title = "邮件类型")
|
||||
var type: String? = null
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package cn.bunny.dao.dto.email
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import jakarta.validation.constraints.NotBlank
|
||||
import jakarta.validation.constraints.NotNull
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
/**
|
||||
* 添加邮箱用户
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Schema(name = "EmailUsersDto", title = "邮箱用户发送基础内容", description = "邮箱用户发送基础内容")
|
||||
class EmailUsersDto {
|
||||
@Schema(name = "id", title = "主键")
|
||||
var id: @NotBlank(message = "id不能为空") Long? = null
|
||||
|
||||
@Schema(name = "email", title = "邮箱")
|
||||
var email: @NotBlank(message = "邮箱不能为空") String? = null
|
||||
|
||||
@Schema(name = "password", title = "密码")
|
||||
var password: @NotBlank(message = "密码不能为空") String? = null
|
||||
|
||||
@Schema(name = "host", title = "SMTP服务器")
|
||||
var host: String? = null
|
||||
|
||||
@Schema(name = "port", title = "端口号")
|
||||
var port: @NotNull(message = "端口号不能为空") Int? = null
|
||||
|
||||
@Schema(name = "smtpAgreement", title = "邮箱协议")
|
||||
var smtpAgreement: Int? = null
|
||||
|
||||
@Schema(name = "isDefault", title = "是否为默认邮件")
|
||||
var isDefault: Boolean? = null
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.bunny.dao.dto.user
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import jakarta.validation.constraints.NotBlank
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Schema(name = "LoginDto", title = "登录表单内容", description = "登录表单内容")
|
||||
class LoginDto {
|
||||
@Schema(name = "username", title = "用户名")
|
||||
var username: @NotBlank(message = "用户名不能为空") String? = null
|
||||
|
||||
@Schema(name = "password", title = "密码")
|
||||
var password: @NotBlank(message = "密码不能为空") String? = null
|
||||
|
||||
@Schema(name = "emailCode", title = "邮箱验证码")
|
||||
var emailCode: @NotBlank(message = "邮箱验证码不能为空") String? = null
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package cn.bunny.dao.entity
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField
|
||||
import com.baomidou.mybatisplus.annotation.*
|
||||
import com.fasterxml.jackson.annotation.JsonFormat
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
|
||||
import io.swagger.annotations.ApiModelProperty
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Data
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Data
|
||||
@Schema(name = "BaseEntity", title = "基础实体类型", description = "基础实体类型")
|
||||
open class BaseEntity : Serializable {
|
||||
@Schema(name = "id", title = "唯一标识")
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@JSONField(serializeUsing = ToStringSerializer::class)
|
||||
var id: Long? = null
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
@Schema(name = "createTime", title = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
var createTime: LocalDateTime? = null
|
||||
|
||||
@Schema(name = "updateTime", title = "更新时间")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
var updateTime: LocalDateTime? = null
|
||||
|
||||
@Schema(name = "createUser", title = "创建用户")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@JSONField(serializeUsing = ToStringSerializer::class)
|
||||
var createUser: Long? = null
|
||||
|
||||
@Schema(name = "updateUser", title = "操作用户")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@JSONField(serializeUsing = ToStringSerializer::class)
|
||||
var updateUser: Long? = null
|
||||
|
||||
@Schema(name = "isDeleted", title = "是否被删除")
|
||||
@TableLogic
|
||||
var isDeleted: Boolean? = null
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package cn.bunny.dao.entity.email
|
||||
|
||||
import cn.bunny.dao.entity.BaseEntity
|
||||
import com.baomidou.mybatisplus.annotation.TableName
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Getter
|
||||
import lombok.Setter
|
||||
import lombok.experimental.Accessors
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-05-19
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@TableName("email_template")
|
||||
@Schema(name = "EmailTemplate", title = "邮件模板", description = "邮件模板")
|
||||
class EmailTemplate : BaseEntity() {
|
||||
@Schema(name = "templateName", title = "模板名称")
|
||||
var templateName: String? = null
|
||||
|
||||
@Schema(name = "subject", title = "主题")
|
||||
var subject: String? = null
|
||||
|
||||
@Schema(name = "body", title = "邮件内容")
|
||||
var body: String? = null
|
||||
|
||||
@Schema(name = "type", title = "邮件类型")
|
||||
var type: String? = null
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package cn.bunny.dao.entity.email
|
||||
|
||||
import cn.bunny.dao.entity.BaseEntity
|
||||
import com.baomidou.mybatisplus.annotation.TableName
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Getter
|
||||
import lombok.Setter
|
||||
import lombok.experimental.Accessors
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 邮箱发送表
|
||||
*
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-05-17
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@TableName("email_users")
|
||||
@Schema(name = "EmailUsers对象", title = "邮箱发送表", description = "邮箱发送表")
|
||||
class EmailUsers : BaseEntity() {
|
||||
@Schema(name = "email", title = "邮箱")
|
||||
var email: String? = null
|
||||
|
||||
@Schema(name = "password", title = "密码")
|
||||
var password: String? = null
|
||||
|
||||
@Schema(name = "host", title = "Host地址")
|
||||
var host: String? = null
|
||||
|
||||
@Schema(name = "port", title = "端口号")
|
||||
var port: Int? = null
|
||||
|
||||
@Schema(name = "smtpAgreement", title = "邮箱协议")
|
||||
var smtpAgreement: String? = null
|
||||
|
||||
@Schema(name = "isDefault", title = "是否为默认邮件")
|
||||
var isDefault: Int? = null
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package cn.bunny.dao.entity.i18n
|
||||
|
||||
import cn.bunny.dao.entity.BaseEntity
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Getter
|
||||
import lombok.Setter
|
||||
import lombok.experimental.Accessors
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 多语言表
|
||||
*
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@Schema(name = "I18n对象", title = "多语言表", description = "多语言表")
|
||||
class I18n : BaseEntity() {
|
||||
@Schema(name = "typeId", title = "语言类型id")
|
||||
var typeId: Long? = null
|
||||
|
||||
@Schema(name = "keyName", title = "多语言key")
|
||||
var keyName: String? = null
|
||||
|
||||
@Schema(name = "summary", title = "翻译")
|
||||
var summary: String? = null
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.bunny.dao.entity.i18n
|
||||
|
||||
import cn.bunny.dao.entity.BaseEntity
|
||||
import com.baomidou.mybatisplus.annotation.TableName
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Getter
|
||||
import lombok.Setter
|
||||
import lombok.experimental.Accessors
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 多语言类型表
|
||||
*
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@TableName("i18n_type")
|
||||
@Schema(name = "I18nType", title = "多语言类型表", description = "多语言类型表")
|
||||
class I18nType : BaseEntity() {
|
||||
@Schema(name = "languageName", title = "语言名称")
|
||||
var languageName: String? = null
|
||||
|
||||
@Schema(name = "summary", title = "语言名")
|
||||
var summary: String? = null
|
||||
|
||||
@Schema(name = "isDefault", title = "是否作为默认语言")
|
||||
var isDefault: Byte? = null
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package cn.bunny.dao.entity.user
|
||||
|
||||
import cn.bunny.dao.entity.BaseEntity
|
||||
import com.baomidou.mybatisplus.annotation.TableName
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Getter
|
||||
import lombok.Setter
|
||||
import lombok.experimental.Accessors
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 用户信息
|
||||
*
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@TableName("sys_user")
|
||||
@Schema(name = "AdminUser对象", title = "用户信息", description = "用户信息")
|
||||
class AdminUser : BaseEntity() {
|
||||
@Schema(name = "username", title = "用户名")
|
||||
var username: String? = null
|
||||
|
||||
@Schema(name = "nickName", title = "昵称")
|
||||
var nickName: String? = null
|
||||
|
||||
@Schema(name = "email", title = "邮箱")
|
||||
var email: String? = null
|
||||
|
||||
@Schema(name = "phone", title = "手机号")
|
||||
var phone: String? = null
|
||||
|
||||
@Schema(name = "password", title = "密码")
|
||||
var password: String? = null
|
||||
|
||||
@Schema(name = "avatar", title = "头像")
|
||||
var avatar: String? = null
|
||||
|
||||
@Schema(name = "sex", title = "性别", description = "0:女 1:男")
|
||||
var sex: Byte? = null
|
||||
|
||||
@Schema(name = "summary", title = "个人描述")
|
||||
var summary: String? = null
|
||||
|
||||
@Schema(name = "lastLoginIp", title = "最后登录IP")
|
||||
var lastLoginIp: String? = null
|
||||
|
||||
@Schema(name = "lastLoginIpAddress", title = "最后登录ip归属地")
|
||||
var lastLoginIpAddress: String? = null
|
||||
|
||||
@Schema(name = "status", title = "状态", description = "1:禁用 0:正常")
|
||||
var status: Byte? = null
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
@Data
|
||||
class ExceptionConstant {
|
||||
companion object {
|
||||
const val UNKNOWN_EXCEPTION: String = "未知错误"
|
||||
|
||||
// 用户相关
|
||||
const val USER_NOT_LOGIN_EXCEPTION: String = "用户未登录"
|
||||
const val USERNAME_IS_EMPTY_EXCEPTION: String = "用户名不能为空"
|
||||
const val ALREADY_USER_EXCEPTION: String = "用户已存在"
|
||||
const val USER_NOT_FOUND_EXCEPTION: String = "用户不存在"
|
||||
|
||||
// 密码相关
|
||||
const val PASSWORD_EXCEPTION: String = "密码错误"
|
||||
const val PASSWORD_NOT_EMPTY_EXCEPTION: String = "密码不能为空"
|
||||
const val OLD_PASSWORD_EXCEPTION: String = "旧密码不匹配"
|
||||
const val PASSWORD_EDIT_EXCEPTION: String = "密码修改失败"
|
||||
const val OLD_PASSWORD_SAME_NEW_PASSWORD_EXCEPTION: String = "旧密码与新密码相同"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
@Data
|
||||
class FileMessageConstant {
|
||||
companion object {
|
||||
const val STORAGE_OBJECT_EXCEPTION: String = "对象错误"
|
||||
const val GET_BUCKET_EXCEPTION: String = "获取文件信息失败"
|
||||
const val FILE_UPLOAD_EXCEPTION: String = "文件上传失败"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
@Data
|
||||
class LocalDateTimeConstant {
|
||||
companion object {
|
||||
const val YYYY_MM_DD: String = "yyyy-MM-dd"
|
||||
const val YYYY_MM_DD_HH_MM_SS: String = "yyyy-MM-dd HH:mm:ss"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
/**
|
||||
* 邮箱消息
|
||||
*/
|
||||
@Data
|
||||
class MailMessageConstant {
|
||||
companion object {
|
||||
const val EMPTY_SEND_OBJECT: String = "空发送对象"
|
||||
const val ADDRESS_NOT_NULL: String = "收件人不能为空"
|
||||
const val TITLE_NOT_NULL: String = "标题不能为空"
|
||||
const val SEND_MESSAGE_NOT_NULL: String = "发送消息不能为空"
|
||||
const val EMAIL_CONFIG_NOT_FOUND: String = "邮箱配置为空"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
@Data
|
||||
class MinioConstant {
|
||||
companion object {
|
||||
private const val FAVICON: String = "favicon"
|
||||
private const val AVATAR: String = "avatar"
|
||||
private const val ARTICLE: String = "article"
|
||||
private const val CAROUSEL: String = "carousel"
|
||||
private const val FEEDBACK: String = "feedback"
|
||||
private const val ARTICLE_COVERS: String = "articleCovers"
|
||||
private const val ARTICLE_ATTACHMENT: String = "articleAttachment"
|
||||
private val typeMap: MutableMap<String, String> = HashMap()
|
||||
|
||||
init {
|
||||
typeMap[FAVICON] = "/favicon/"
|
||||
typeMap[AVATAR] = "/avatar/"
|
||||
typeMap[ARTICLE] = "/article/"
|
||||
typeMap[CAROUSEL] = "/carousel/"
|
||||
typeMap[FEEDBACK] = "/feedback/"
|
||||
typeMap["articleImages"] = "/articleImages/"
|
||||
typeMap["articleVideo"] = "/articleVideo/"
|
||||
typeMap[ARTICLE_COVERS] = "/articleCovers/"
|
||||
typeMap[ARTICLE_ATTACHMENT] = "/articleAttachment/"
|
||||
typeMap["images"] = "/images/"
|
||||
typeMap["video"] = "/video/"
|
||||
typeMap["default"] = "/default/"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getType(type: String): String {
|
||||
val value = typeMap[type]
|
||||
if (value != null) return value
|
||||
throw RuntimeException(FileMessageConstant.STORAGE_OBJECT_EXCEPTION)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
/**
|
||||
* Redis用户前缀设置
|
||||
*/
|
||||
@Data
|
||||
class RedisUserConstant {
|
||||
companion object {
|
||||
// 管理员用户
|
||||
private const val ADMIN_LOGIN_INFO_PREFIX: String = "ADMIN::LOGIN_INFO::"
|
||||
private const val ADMIN_EMAIL_CODE_PREFIX: String = "ADMIN::EMAIL_CODE::"
|
||||
|
||||
// 普通用户
|
||||
private const val USER_LOGIN_INFO_PREFIX: String = "USER::LOGIN_INFO::"
|
||||
private const val USER_EMAIL_CODE_PREFIX: String = "USER::EMAIL_CODE::"
|
||||
private const val USER_DO_LIKE_PREFIX: String = "USER::doLike::"
|
||||
|
||||
/**
|
||||
* * 管理员用户登录信息
|
||||
*
|
||||
* @param adminUser 管理员用户
|
||||
* @return 登录信息key
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getAdminLoginInfoPrefix(adminUser: String): String {
|
||||
return ADMIN_LOGIN_INFO_PREFIX + adminUser
|
||||
}
|
||||
|
||||
/**
|
||||
* * 管理员用户邮箱验证码
|
||||
*
|
||||
* @param adminUser 管理员用户
|
||||
* @return 管理员用户邮箱验证码key
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getAdminUserEmailCodePrefix(adminUser: String): String {
|
||||
return ADMIN_EMAIL_CODE_PREFIX + adminUser
|
||||
}
|
||||
|
||||
/**
|
||||
* * 用户登录信息
|
||||
*
|
||||
* @param user 用户名
|
||||
* @return 登录信息key
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getUserLoginInfoPrefix(user: String): String {
|
||||
return USER_LOGIN_INFO_PREFIX + user
|
||||
}
|
||||
|
||||
/**
|
||||
* * 用户邮箱验证码
|
||||
*
|
||||
* @param user 用户名
|
||||
* @return 用户邮箱验证码key
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getUserEmailCodePrefix(user: String): String {
|
||||
return USER_EMAIL_CODE_PREFIX + user
|
||||
}
|
||||
|
||||
/**
|
||||
* * 用户点赞操作
|
||||
*
|
||||
* @param user 用户名
|
||||
* @return 用户点赞key
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getUserDoLikePrefix(user: String): String {
|
||||
return USER_DO_LIKE_PREFIX + user
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.bunny.dao.pojo.constant
|
||||
|
||||
import lombok.Data
|
||||
|
||||
@Data
|
||||
class UserConstant {
|
||||
companion object {
|
||||
const val USER_AVATAR: String =
|
||||
"https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.bunny.dao.pojo.email
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
|
||||
/**
|
||||
* 邮件发送对象
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(name = "EmailSend", title = "邮件发送表单", description = "邮件发送表单")
|
||||
class EmailSend {
|
||||
|
||||
@Schema(name = "sendTo", title = "给谁发送")
|
||||
var sendTo: String? = null
|
||||
|
||||
@Schema(name = "subject", title = "发送主题")
|
||||
var subject: String? = null
|
||||
|
||||
@Schema(name = "isRichText", title = "是否为富文本")
|
||||
var isRichText: Boolean? = null
|
||||
|
||||
@Schema(name = "message", title = "发送内容")
|
||||
var message: String? = null
|
||||
|
||||
@Schema(name = "ccParam", title = "抄送人")
|
||||
var ccParam: String? = null
|
||||
|
||||
@Schema(name = "file", title = "发送的文件")
|
||||
var file: Array<MultipartFile>? = null
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package cn.bunny.dao.pojo.email
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
/**
|
||||
* 邮箱发送初始化参数
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(name = "EmailSendInit", title = "邮件发送初始化", description = "邮件发送初始化")
|
||||
class EmailSendInit {
|
||||
|
||||
@Schema(name = "port", title = "端口")
|
||||
var port: Int? = null
|
||||
|
||||
@Schema(name = "host", title = "主机")
|
||||
var host: String? = null
|
||||
|
||||
@Schema(name = "username", title = "用户名")
|
||||
var username: String? = null
|
||||
|
||||
@Schema(name = "password", title = "密码")
|
||||
var password: String? = null
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package cn.bunny.dao.pojo.enums
|
||||
|
||||
/**
|
||||
* 数据库操作类型
|
||||
*/
|
||||
enum class OperationType {
|
||||
UPDATE, INSERT
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package cn.bunny.dao.pojo.file
|
||||
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
class MinioFIlePath {
|
||||
var filename: String? = null
|
||||
var uuidFilename: String? = null
|
||||
var timeUuidFilename: String? = null
|
||||
var filepath: String? = null
|
||||
var bucketNameFilepath: String? = null
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package cn.bunny.dao.pojo.result
|
||||
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* 封装分页查询结果
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
class PageResult<T> : Serializable {
|
||||
// 当前页
|
||||
var pageNo: Int? = null
|
||||
|
||||
// 每页记录数
|
||||
var pageSize: Int? = null
|
||||
|
||||
// 总记录数
|
||||
var total: Long = 0
|
||||
|
||||
// 当前页数据集合
|
||||
var list: List<T>? = null
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
package cn.bunny.dao.pojo.result;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Result<T> {
|
||||
// 状态码
|
||||
private Integer code;
|
||||
// 返回消息
|
||||
private String message;
|
||||
// 返回数据
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* * 自定义返回体
|
||||
*
|
||||
* @param data 返回体
|
||||
* @return Result<T>
|
||||
*/
|
||||
protected static <T> Result<T> build(T data) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 自定义返回体,使用ResultCodeEnum构建
|
||||
*
|
||||
* @param body 返回体
|
||||
* @param codeEnum 返回状态码
|
||||
* @return Result<T>
|
||||
*/
|
||||
public static <T> Result<T> build(T body, ResultCodeEnum codeEnum) {
|
||||
Result<T> result = build(body);
|
||||
result.setCode(codeEnum.getCode());
|
||||
result.setMessage(codeEnum.getMessage());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 自定义返回体
|
||||
*
|
||||
* @param body 返回体
|
||||
* @param code 返回状态码
|
||||
* @param message 返回消息
|
||||
* @return Result<T>
|
||||
*/
|
||||
public static <T> Result<T> build(T body, Integer code, String message) {
|
||||
Result<T> result = build(body);
|
||||
result.setCode(code);
|
||||
result.setMessage(message);
|
||||
result.setData(null);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作成功
|
||||
*
|
||||
* @return Result<T>
|
||||
*/
|
||||
public static <T> Result<T> success() {
|
||||
return success(null, ResultCodeEnum.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作成功
|
||||
*
|
||||
* @param data baseCategory1List
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
return build(data, ResultCodeEnum.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作成功-状态码
|
||||
*
|
||||
* @param codeEnum 状态码
|
||||
*/
|
||||
public static <T> Result<T> success(ResultCodeEnum codeEnum) {
|
||||
return success(null, codeEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作成功-自定义返回数据和状态码
|
||||
*
|
||||
* @param data 返回体
|
||||
* @param codeEnum 状态码
|
||||
*/
|
||||
public static <T> Result<T> success(T data, ResultCodeEnum codeEnum) {
|
||||
return build(data, codeEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-自定义返回数据和状态码
|
||||
*
|
||||
* @param data 返回体
|
||||
* @param message 错误信息
|
||||
*/
|
||||
public static <T> Result<T> success(T data, String message) {
|
||||
return build(data, 200, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-自定义返回数据和状态码
|
||||
*
|
||||
* @param data 返回体
|
||||
* @param code 状态码
|
||||
* @param message 错误信息
|
||||
*/
|
||||
public static <T> Result<T> success(T data, Integer code, String message) {
|
||||
return build(data, code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败
|
||||
*/
|
||||
public static <T> Result<T> error() {
|
||||
return Result.build(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-自定义返回数据
|
||||
*
|
||||
* @param data 返回体
|
||||
*/
|
||||
public static <T> Result<T> error(T data) {
|
||||
return build(data, ResultCodeEnum.FAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-状态码
|
||||
*
|
||||
* @param codeEnum 状态码
|
||||
*/
|
||||
public static <T> Result<T> error(ResultCodeEnum codeEnum) {
|
||||
return build(null, codeEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-自定义返回数据和状态码
|
||||
*
|
||||
* @param data 返回体
|
||||
* @param codeEnum 状态码
|
||||
*/
|
||||
public static <T> Result<T> error(T data, ResultCodeEnum codeEnum) {
|
||||
return build(data, codeEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-自定义返回数据和状态码
|
||||
*
|
||||
* @param data 返回体
|
||||
* @param code 状态码
|
||||
* @param message 错误信息
|
||||
*/
|
||||
public static <T> Result<T> error(T data, Integer code, String message) {
|
||||
return build(data, code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 操作失败-自定义返回数据和状态码
|
||||
*
|
||||
* @param data 返回体
|
||||
* @param message 错误信息
|
||||
*/
|
||||
public static <T> Result<T> error(T data, String message) {
|
||||
return build(null, 500, message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package cn.bunny.dao.pojo.result
|
||||
|
||||
import lombok.Getter
|
||||
|
||||
/**
|
||||
* 统一返回结果状态信息类
|
||||
*/
|
||||
@Getter
|
||||
enum class ResultCodeEnum(val code: Int, val message: String) {
|
||||
// 成功操作 200
|
||||
SUCCESS(200, "操作成功"),
|
||||
SUCCESS_LOGOUT(200, "退出成功"),
|
||||
EMAIL_CODE_REFRESH(200, "邮箱验证码已刷新"),
|
||||
|
||||
// 验证错误 201
|
||||
USERNAME_OR_PASSWORD_NOT_EMPTY(201, "用户名或密码不能为空"),
|
||||
EMAIL_CODE_NOT_EMPTY(201, "邮箱验证码不能为空"),
|
||||
SEND_EMAIL_CODE_NOT_EMPTY(201, "请先发送邮箱验证码"),
|
||||
EMAIL_CODE_NOT_MATCHING(201, "邮箱验证码不匹配"),
|
||||
LOGIN_ERROR(201, "账号或密码错误"),
|
||||
LOGIN_ERROR_USERNAME_PASSWORD_NOT_EMPTY(201, "登录信息不能为空"),
|
||||
|
||||
// 数据相关 206
|
||||
ILLEGAL_REQUEST(206, "非法请求"),
|
||||
REPEAT_SUBMIT(206, "重复提交"),
|
||||
DATA_ERROR(206, "数据异常"),
|
||||
|
||||
// 身份过期 208
|
||||
LOGIN_AUTH(208, "请先登陆"),
|
||||
AUTHENTICATION_EXPIRED(208, "身份验证过期"),
|
||||
SESSION_EXPIRATION(208, "会话过期"),
|
||||
|
||||
// 封禁 209
|
||||
FAIL_NO_ACCESS_DENIED_USER_LOCKED(209, "该账户被封禁"),
|
||||
THE_SAME_USER_HAS_LOGGED_IN(209, "相同用户已登录"),
|
||||
|
||||
// 提示错误
|
||||
URL_ENCODE_ERROR(216, "URL编码失败"),
|
||||
ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"),
|
||||
FETCH_USERINFO_ERROR(219, "获取用户信息失败"),
|
||||
|
||||
// 无权访问 403
|
||||
FAIL_REQUEST_NOT_AUTH(403, "用户未认证"),
|
||||
FAIL_NO_ACCESS_DENIED(403, "无权访问"),
|
||||
FAIL_NO_ACCESS_DENIED_USER_OFFLINE(403, "用户强制下线"),
|
||||
LOGGED_IN_FROM_ANOTHER_DEVICE(403, "没有权限访问"),
|
||||
|
||||
// 系统错误 500
|
||||
SERVICE_ERROR(500, "服务异常"),
|
||||
FAIL(500, "失败"),
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package cn.bunny.dao.vo
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField
|
||||
import com.fasterxml.jackson.annotation.JsonFormat
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.Data
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Data
|
||||
@Schema(name = "BaseVo", title = "基础返回对象内容", description = "基础返回对象内容")
|
||||
open class BaseVo : Serializable {
|
||||
@Schema(name = "id", title = "主键")
|
||||
@JsonProperty("id")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@JSONField(serializeUsing = ToStringSerializer::class)
|
||||
var id: Long? = null
|
||||
|
||||
@Schema(name = "updateTime", title = "更新时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonSerialize(using = LocalDateTimeSerializer::class)
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer::class)
|
||||
var updateTime: LocalDateTime? = null
|
||||
|
||||
@Schema(name = "createTime", title = "发布时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonSerialize(using = LocalDateTimeSerializer::class)
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer::class)
|
||||
var createTime: LocalDateTime? = null
|
||||
|
||||
@Schema(name = "createUser", title = "创建用户")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@JSONField(serializeUsing = ToStringSerializer::class)
|
||||
var createUser: Long? = null
|
||||
|
||||
@Schema(name = "updateUser", title = "操作用户")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@JSONField(serializeUsing = ToStringSerializer::class)
|
||||
var updateUser: Long? = null
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.bunny.dao.vo.email
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Schema(name = "EmailTemplateVo对象", title = "邮箱模板返回内容", description = "邮箱模板返回内容")
|
||||
class EmailTemplateVo {
|
||||
@Schema(name = "templateName", title = "模板名称")
|
||||
var templateName: String? = null
|
||||
|
||||
@Schema(name = "subject", title = "主题")
|
||||
var subject: String? = null
|
||||
|
||||
@Schema(name = "body", title = "邮件内容")
|
||||
var body: String? = null
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package cn.bunny.dao.vo.user
|
||||
|
||||
import cn.bunny.dao.vo.BaseVo
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.*
|
||||
|
||||
/**
|
||||
* 用户登录返回内容
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Schema(name = "LoginVo对象", title = "登录成功返回内容", description = "登录成功返回内容")
|
||||
class LoginVo : BaseVo() {
|
||||
@Schema(name = "nickName", title = "昵称")
|
||||
var nickName: String? = null
|
||||
|
||||
@Schema(name = "username", title = "用户名")
|
||||
var username: String? = null
|
||||
|
||||
@Schema(name = "email", title = "邮箱")
|
||||
var email: String? = null
|
||||
|
||||
@Schema(name = "phone", title = "手机号")
|
||||
var phone: String? = null
|
||||
|
||||
@Schema(name = "password", title = "密码")
|
||||
var password: String? = null
|
||||
|
||||
@Schema(name = "avatar", title = "头像")
|
||||
var avatar: String? = null
|
||||
|
||||
@Schema(name = "sex", title = "0:女 1:男")
|
||||
var sex: Byte? = null
|
||||
|
||||
@Schema(name = "personDescription", title = "个人描述")
|
||||
var personDescription: String? = null
|
||||
|
||||
@Schema(name = "articleMode", title = "文章显示模式")
|
||||
var articleMode: String? = null
|
||||
|
||||
@Schema(name = "layout", title = "页面布局方式")
|
||||
var layout: String? = null
|
||||
|
||||
@Schema(name = "lastLoginIp", title = "最后登录IP")
|
||||
var lastLoginIp: String? = null
|
||||
|
||||
@Schema(name = "lastLoginIpAddress", title = "最后登录ip地址")
|
||||
var lastLoginIpAddress: String? = null
|
||||
|
||||
@Schema(name = "totalIntegral", title = "积分")
|
||||
var totalIntegral: Int? = null
|
||||
|
||||
@Schema(name = "currentIntegral", title = "当前积分")
|
||||
var currentIntegral: Int? = null
|
||||
|
||||
@Schema(name = "status", title = "0:禁用 1:正常")
|
||||
var status: Boolean? = null
|
||||
|
||||
@Schema(name = "token", title = "令牌")
|
||||
var token: String? = null
|
||||
|
||||
@Schema(name = "roleList", title = "角色列表")
|
||||
var roleList: List<String>? = null
|
||||
|
||||
@Schema(name = "powerList", title = "权限列表")
|
||||
var powerList: List<String>? = null
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.dao.vo.user
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import lombok.AllArgsConstructor
|
||||
import lombok.Builder
|
||||
import lombok.Data
|
||||
import lombok.NoArgsConstructor
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(name = "ValidateCodeVo", title = "验证码响应结果实体类", description = "验证码响应结果实体类")
|
||||
class ValidateCodeVo {
|
||||
@Schema(name = "codeKey", title = "验证码key")
|
||||
var codeKey: String? = null
|
||||
|
||||
@Schema(name = "codeValue", title = "验证码value")
|
||||
var codeValue: String? = null
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>kotlin-single</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>bunny-template</name>
|
||||
<description>bunny-template</description>
|
||||
|
||||
<modules>
|
||||
<module>services</module>
|
||||
<module>common</module>
|
||||
<module>dao</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<java.version>17</java.version>
|
||||
<junit.version>3.8.1</junit.version>
|
||||
<mybatis-plus.version>3.5.6</mybatis-plus.version>
|
||||
<mysql.version>8.0.30</mysql.version>
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
<fastjson2.version>2.0.47</fastjson2.version>
|
||||
<minio.version>8.5.9</minio.version>
|
||||
<lombok.version>1.18.32</lombok.version>
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<easyexcel.version>3.3.3</easyexcel.version>
|
||||
<jodatime.version>2.10.1</jodatime.version>
|
||||
<aspectj>1.9.21</aspectj>
|
||||
<pagehelper.version>6.1.0</pagehelper.version>
|
||||
<velocity.version>2.2</velocity.version>
|
||||
<velocity-tools.version>3.1</velocity-tools.version>
|
||||
<HikariCP.version>5.1.0</HikariCP.version>
|
||||
<dynamic.datasource.version>4.3.1</dynamic.datasource.version>
|
||||
<kotlin.version>2.0.20</kotlin.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 单元测试 -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
<!-- velocity -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>${velocity.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity.tools</groupId>
|
||||
<artifactId>velocity-tools-generic</artifactId>
|
||||
<version>${velocity-tools.version}</version>
|
||||
</dependency>
|
||||
<!-- mybatis-plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
<!-- mysql连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>${HikariCP.version}</version>
|
||||
</dependency>
|
||||
<!-- 多数据库源插件 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
<version>${dynamic.datasource.version}</version>
|
||||
</dependency>
|
||||
<!-- knife4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
<!-- fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${fastjson2.version}</version>
|
||||
</dependency>
|
||||
<!-- minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio.version}</version>
|
||||
</dependency>
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
<!-- hutool -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.27</version>
|
||||
</dependency>
|
||||
<!--jjwt-->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
</dependency>
|
||||
<!-- Excel表操作 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>${easyexcel.version}</version>
|
||||
</dependency>
|
||||
<!-- aspectj -->
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>${aspectj}</version>
|
||||
</dependency>
|
||||
<!-- aspectj -->
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>${aspectj}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>${jodatime.version}</version>
|
||||
</dependency>
|
||||
<!-- fasterxml -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.12.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<jvmTarget>${maven.compiler.target}</jvmTarget>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,21 @@
|
|||
FROM openjdk:17
|
||||
MAINTAINER bunny
|
||||
|
||||
#系统编码
|
||||
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
|
||||
# 设置时区,构建镜像时执行的命令
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo "Asia/Shanghai" > /etc/timezone
|
||||
|
||||
# 设定工作目录
|
||||
WORKDIR /home/bunny
|
||||
|
||||
# 复制jar包
|
||||
COPY target/*.jar /home/bunny/app.jar
|
||||
|
||||
#启动容器时的进程
|
||||
ENTRYPOINT ["java","-jar","/home/bunny/app.jar"]
|
||||
|
||||
#暴露 8800 端口
|
||||
EXPOSE 8800
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.3</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>services</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>services</name>
|
||||
<description>services</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
<kotlin.compiler.jvmTarget>17</kotlin.compiler.jvmTarget>
|
||||
<java.version>17</java.version>
|
||||
<kotlin.version>2.0.20</kotlin.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>common-service</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<!-- spring-security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<!-- spring-security-test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
</dependency>
|
||||
<!-- amqp -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.16.0-rc1</version>
|
||||
</dependency>
|
||||
<!-- asp 切面 -->
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
<!-- websocket -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<!-- 多数据库源插件 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
<version>4.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
<compilerPlugins>
|
||||
<plugin>spring</plugin>
|
||||
</compilerPlugins>
|
||||
<jvmTarget>${java.version}</jvmTarget>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>testCompile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,28 @@
|
|||
package cn.bunny.services
|
||||
|
||||
import lombok.extern.slf4j.Slf4j
|
||||
import org.mybatis.spring.annotation.MapperScan
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.cache.annotation.EnableCaching
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement
|
||||
|
||||
@MapperScan("cn.bunny.services.mapper")
|
||||
@ComponentScan("cn.bunny")
|
||||
@EnableScheduling
|
||||
@EnableCaching
|
||||
@EnableTransactionManagement
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
class ServicesApplication {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<ServicesApplication>(*args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.services.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/i18n")
|
||||
@Tag(name = "多语言", description = "多语言相关接口")
|
||||
public class I18nController {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.services.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言类型表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/i18nType")
|
||||
@Tag(name = "多语言类型", description = "多语言类型相关接口")
|
||||
public class I18nTypeController {
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.bunny.services.controller
|
||||
|
||||
import cn.hutool.captcha.CaptchaUtil
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@Tag(name = "访问首页内容", description = "访问首页内容相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
class IndexController {
|
||||
@Operation(summary = "访问首页", description = "访问首页")
|
||||
@GetMapping("")
|
||||
fun index(): String {
|
||||
return "欢迎访问 Bunny Java Template,欢迎去Gitee:https://gitee.com/BunnyBoss/java_single.git"
|
||||
}
|
||||
|
||||
@Operation(summary = "生成验证码", description = "生成验证码")
|
||||
@GetMapping("noAuth/checkCode")
|
||||
fun checkCode(): ResponseEntity<ByteArray> {
|
||||
val headers = HttpHeaders()
|
||||
headers.contentType = MediaType.IMAGE_JPEG
|
||||
// 生成验证码
|
||||
val captcha = CaptchaUtil.createCircleCaptcha(150, 48, 4, 2)
|
||||
val image = captcha.imageBytes
|
||||
return ResponseEntity(image, headers, HttpStatus.OK)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package cn.bunny.services.controller
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 用户信息 前端控制器
|
||||
*
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@Tag(name = "系统用户", description = "系统用户相关接口")
|
||||
class UserController
|
|
@ -0,0 +1,18 @@
|
|||
package cn.bunny.services.mapper;
|
||||
|
||||
import cn.bunny.dao.entity.i18n.I18n;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface I18nMapper extends BaseMapper<I18n> {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package cn.bunny.services.mapper;
|
||||
|
||||
import cn.bunny.dao.entity.i18n.I18nType;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言类型表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface I18nTypeMapper extends BaseMapper<I18nType> {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package cn.bunny.services.mapper;
|
||||
|
||||
import cn.bunny.dao.entity.user.AdminUser;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户信息 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<AdminUser> {
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package cn.bunny.services.security.config
|
||||
|
||||
import cn.bunny.services.security.custom.CustomPasswordEncoder
|
||||
import cn.bunny.services.security.filter.TokenAuthenticationFilter
|
||||
import cn.bunny.services.security.filter.TokenLoginFilterService
|
||||
import cn.bunny.services.security.handelr.SecurityAccessDeniedHandler
|
||||
import cn.bunny.services.security.handelr.SecurityAuthenticationEntryPoint
|
||||
import cn.bunny.services.security.service.CustomAuthorizationManagerService
|
||||
import cn.bunny.services.security.service.CustomUserDetailsService
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.data.redis.core.RedisTemplate
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
|
||||
import org.springframework.security.config.annotation.web.configurers.*
|
||||
import org.springframework.security.core.session.SessionRegistry
|
||||
import org.springframework.security.core.session.SessionRegistryImpl
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
open class WebSecurityConfig {
|
||||
@Autowired
|
||||
private val redisTemplate: RedisTemplate<Any, Any>? = null
|
||||
|
||||
// 自定义用户接口
|
||||
@Autowired
|
||||
private val customUserDetailsService: CustomUserDetailsService? = null
|
||||
|
||||
// 自定义密码加密器
|
||||
@Autowired
|
||||
private val customPasswordEncoder: CustomPasswordEncoder? = null
|
||||
|
||||
// 自定义验证码
|
||||
@Autowired
|
||||
private val customAuthorizationManager: CustomAuthorizationManagerService? = null
|
||||
|
||||
@Autowired
|
||||
private val authenticationConfiguration: AuthenticationConfiguration? = null
|
||||
|
||||
@Bean
|
||||
@Throws(Exception::class)
|
||||
open fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain {
|
||||
httpSecurity // 前端段分离不需要---禁用明文验证
|
||||
.httpBasic { obj: HttpBasicConfigurer<HttpSecurity> -> obj.disable() } // 前端段分离不需要---禁用默认登录页
|
||||
.formLogin { obj: FormLoginConfigurer<HttpSecurity> -> obj.disable() } // 前端段分离不需要---禁用退出页
|
||||
.logout { obj: LogoutConfigurer<HttpSecurity> -> obj.disable() } // 前端段分离不需要---csrf攻击
|
||||
.csrf { obj: CsrfConfigurer<HttpSecurity> -> obj.disable() } // 跨域访问权限,如果需要可以关闭后自己配置跨域访问
|
||||
.cors { obj: CorsConfigurer<HttpSecurity> -> obj.disable() } // 前后端分离不需要---因为是无状态的
|
||||
.sessionManagement { obj: SessionManagementConfigurer<HttpSecurity> -> obj.disable() } // 前后端分离不需要---记住我,e -> e.rememberMeParameter("rememberBunny").rememberMeCookieName("rememberBunny").key("BunnyKey")
|
||||
.rememberMe { obj: RememberMeConfigurer<HttpSecurity> -> obj.disable() }
|
||||
.authorizeHttpRequests { authorize ->
|
||||
// 有样式文件,不需要访问权限
|
||||
// authorize.requestMatchers(RegexRequestMatcher.regexMatcher("^\\S*[css|js]$")).permitAll()
|
||||
authorize.requestMatchers(RegexRequestMatcher.regexMatcher("^.*\\.(css|js)$")).permitAll()
|
||||
// 上面都不是需要鉴权访问
|
||||
authorize.anyRequest().access(customAuthorizationManager)
|
||||
}
|
||||
.exceptionHandling { exception: ExceptionHandlingConfigurer<HttpSecurity?> ->
|
||||
// 请求未授权接口
|
||||
exception.authenticationEntryPoint(SecurityAuthenticationEntryPoint())
|
||||
// 没有权限访问
|
||||
exception.accessDeniedHandler(SecurityAccessDeniedHandler())
|
||||
} // 登录验证过滤器
|
||||
.addFilterBefore(
|
||||
TokenLoginFilterService(authenticationConfiguration!!, redisTemplate!!, customUserDetailsService!!),
|
||||
UsernamePasswordAuthenticationFilter::class.java
|
||||
) // 其它权限鉴权过滤器
|
||||
.addFilterAt(
|
||||
TokenAuthenticationFilter(redisTemplate),
|
||||
UsernamePasswordAuthenticationFilter::class.java
|
||||
) // 自定义密码加密器和用户登录
|
||||
.passwordManagement(customPasswordEncoder).userDetailsService(customUserDetailsService)
|
||||
|
||||
return httpSecurity.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun sessionRegistry(): SessionRegistry {
|
||||
return SessionRegistryImpl()
|
||||
}
|
||||
|
||||
// 排出鉴定路径
|
||||
@Bean
|
||||
open fun webSecurityCustomizer(): WebSecurityCustomizer {
|
||||
val annotations = arrayOf(
|
||||
"/", "/ws/**",
|
||||
"/*/*/noAuth/**", "/*/noAuth/**", "/noAuth/**",
|
||||
"/favicon.ico", "*.html",
|
||||
"/swagger-resources/**", "/v3/**", "/swagger-ui/**"
|
||||
)
|
||||
return WebSecurityCustomizer { web: WebSecurity -> web.ignoring().requestMatchers(*annotations) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cn.bunny.services.security.custom
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.Customizer
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.util.DigestUtils
|
||||
|
||||
/**
|
||||
* 自定义密码加密比对
|
||||
*/
|
||||
@Configuration
|
||||
class CustomPasswordEncoder : PasswordEncoder, Customizer<PasswordManagementConfigurer<HttpSecurity?>?> {
|
||||
override fun encode(rawPassword: CharSequence): String {
|
||||
return DigestUtils.md5DigestAsHex(rawPassword.toString().toByteArray())
|
||||
}
|
||||
|
||||
override fun matches(rawPassword: CharSequence, encodedPassword: String): Boolean {
|
||||
return encodedPassword.matches(DigestUtils.md5DigestAsHex(rawPassword.toString().toByteArray()).toRegex())
|
||||
}
|
||||
|
||||
override fun customize(httpSecurityPasswordManagementConfigurer: PasswordManagementConfigurer<HttpSecurity?>?) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cn.bunny.services.security.custom
|
||||
|
||||
import cn.bunny.dao.entity.user.AdminUser
|
||||
import lombok.Getter
|
||||
import lombok.Setter
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.core.userdetails.User
|
||||
|
||||
/**
|
||||
* 重写自带的User
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
class CustomUser(user: AdminUser, authorities: Collection<GrantedAuthority?>?) :
|
||||
User(user.email, user.password, authorities)
|
|
@ -0,0 +1,59 @@
|
|||
package cn.bunny.services.security.filter
|
||||
|
||||
import cn.bunny.common.service.exception.BunnyException
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.data.redis.core.RedisTemplate
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.web.filter.OncePerRequestFilter
|
||||
import java.io.IOException
|
||||
import java.util.function.Consumer
|
||||
|
||||
class TokenAuthenticationFilter(private val redisTemplate: RedisTemplate<Any, Any>) : OncePerRequestFilter() {
|
||||
@Throws(ServletException::class, IOException::class, BunnyException::class)
|
||||
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
|
||||
val token = request.getHeader("token")
|
||||
|
||||
// 判断是否有token,如果后面还有过滤器就这样写
|
||||
// if (token == null) {
|
||||
// doFilter(request, response, chain)
|
||||
// return
|
||||
// }
|
||||
|
||||
|
||||
// 自定义实现内容
|
||||
val authentication = getAuthentication(request)
|
||||
if (authentication != null) {
|
||||
// 设置用户详细信息
|
||||
SecurityContextHolder.getContext().authentication = authentication
|
||||
}
|
||||
|
||||
chain.doFilter(request, response)
|
||||
}
|
||||
|
||||
/**
|
||||
* * 用户请求判断
|
||||
*
|
||||
* @param request 请求
|
||||
* @return 验证码方法
|
||||
*/
|
||||
private fun getAuthentication(request: HttpServletRequest): UsernamePasswordAuthenticationToken {
|
||||
// 请求头是否有token
|
||||
val token = request.getHeader("token")
|
||||
val username = "admin"
|
||||
val authList: MutableList<SimpleGrantedAuthority> = ArrayList()
|
||||
|
||||
// 设置角色内容
|
||||
if (token != null) {
|
||||
val roleList = ArrayList<String>()
|
||||
roleList.forEach(Consumer { role: String? -> authList.add(SimpleGrantedAuthority(role)) })
|
||||
return UsernamePasswordAuthenticationToken(username, null, authList)
|
||||
} else {
|
||||
return UsernamePasswordAuthenticationToken(username, null, ArrayList())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package cn.bunny.services.security.filter
|
||||
|
||||
import cn.bunny.common.service.utils.ResponseUtil.Companion.out
|
||||
import cn.bunny.dao.dto.user.LoginDto
|
||||
import cn.bunny.dao.pojo.constant.RedisUserConstant.Companion.getAdminLoginInfoPrefix
|
||||
import cn.bunny.dao.pojo.constant.RedisUserConstant.Companion.getAdminUserEmailCodePrefix
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import cn.bunny.dao.pojo.result.ResultCodeEnum
|
||||
import cn.bunny.services.security.handelr.SecurityAuthenticationFailureHandler
|
||||
import cn.bunny.services.security.handelr.SecurityAuthenticationSuccessHandler
|
||||
import cn.bunny.services.security.service.CustomUserDetailsService
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.data.redis.core.RedisTemplate
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
/**
|
||||
* * UsernamePasswordAuthenticationFilter
|
||||
* 也可以在这里添加验证码、短信等的验证
|
||||
* 由于SpringSecurity的登录只能是表单形式 并且用户名密码需要时username、password,可以通过继承 UsernamePasswordAuthenticationFilter 获取登录请求的参数
|
||||
* 再去设置到 UsernamePasswordAuthenticationToken 中 来改变请求传参方式、参数名等 或者也可以在登录的时候加入其他参数等等
|
||||
*/
|
||||
class TokenLoginFilterService(
|
||||
authenticationConfiguration: AuthenticationConfiguration,
|
||||
redisTemplate: RedisTemplate<Any, Any>,
|
||||
customUserDetailsService: CustomUserDetailsService
|
||||
) : UsernamePasswordAuthenticationFilter() {
|
||||
private val redisTemplate: RedisTemplate<Any, Any>
|
||||
private val customUserDetailsService: CustomUserDetailsService
|
||||
private lateinit var loginDto: LoginDto
|
||||
|
||||
// 依赖注入
|
||||
init {
|
||||
this.setAuthenticationSuccessHandler(SecurityAuthenticationSuccessHandler())
|
||||
this.setAuthenticationFailureHandler(SecurityAuthenticationFailureHandler())
|
||||
this.setPostOnly(false)
|
||||
// ? 指定登录接口及提交方式,可以指定任意路径
|
||||
this.setRequiresAuthenticationRequestMatcher(AntPathRequestMatcher("/*/login", HttpMethod.POST.name()))
|
||||
this.authenticationManager = authenticationConfiguration.authenticationManager
|
||||
// 依赖注入
|
||||
this.redisTemplate = redisTemplate
|
||||
this.customUserDetailsService = customUserDetailsService
|
||||
}
|
||||
|
||||
/**
|
||||
* * 登录认证,获取输入的用户名和密码,调用方法认证
|
||||
* 接受前端login登录参数
|
||||
* 在这里可以设置短信验证登录
|
||||
*/
|
||||
@Throws(AuthenticationException::class)
|
||||
override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse): Authentication? {
|
||||
try {
|
||||
// 获取用户信息相关内容
|
||||
val loginDto = LoginDto()
|
||||
loginDto.username = "admin"
|
||||
loginDto.password = "password"
|
||||
|
||||
// 封装对象,将用户名密码传入
|
||||
val authenticationToken: Authentication = UsernamePasswordAuthenticationToken(loginDto.username, loginDto.password)
|
||||
return authenticationManager.authenticate(authenticationToken)
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e.localizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* * 认证成功调用方法
|
||||
* 返回登录成功后的信息
|
||||
*/
|
||||
override fun successfulAuthentication(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
chain: FilterChain,
|
||||
auth: Authentication
|
||||
) {
|
||||
// 封装返回对象
|
||||
val loginAdminVo = customUserDetailsService.login(loginDto)
|
||||
|
||||
// 判断用户是否被锁定
|
||||
if (loginAdminVo.status!!) {
|
||||
out(response, Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED))
|
||||
return
|
||||
}
|
||||
|
||||
// 将值存入Redis中
|
||||
redisTemplate.opsForValue()[getAdminLoginInfoPrefix(loginAdminVo.email!!), loginAdminVo, 15] =
|
||||
TimeUnit.DAYS
|
||||
// 将Redis中验证码删除
|
||||
redisTemplate.delete(getAdminUserEmailCodePrefix(loginAdminVo.email!!))
|
||||
|
||||
// 返回登录信息
|
||||
out(response, Result.success(loginAdminVo))
|
||||
}
|
||||
|
||||
/**
|
||||
* * 认证失败调用方法,失败判断
|
||||
* 1. 是否包含用户名
|
||||
* 2. 是否包含密码
|
||||
*/
|
||||
override fun unsuccessfulAuthentication(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
failed: AuthenticationException
|
||||
) {
|
||||
val password = loginDto.password
|
||||
val username = loginDto.username
|
||||
|
||||
when {
|
||||
password.isNullOrBlank() || username.isNullOrBlank() -> out(response, Result.error(ResultCodeEnum.USERNAME_OR_PASSWORD_NOT_EMPTY))
|
||||
else -> out(response, Result.error(null, ResultCodeEnum.LOGIN_ERROR))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cn.bunny.services.security.handelr
|
||||
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import cn.bunny.dao.pojo.result.ResultCodeEnum
|
||||
import com.alibaba.fastjson2.JSON
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import lombok.SneakyThrows
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.web.access.AccessDeniedHandler
|
||||
|
||||
/**
|
||||
* 没有权限访问
|
||||
*/
|
||||
class SecurityAccessDeniedHandler : AccessDeniedHandler {
|
||||
@SneakyThrows
|
||||
override fun handle(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
accessDeniedException: AccessDeniedException
|
||||
) {
|
||||
val result = Result.error<Any>(ResultCodeEnum.FAIL_NO_ACCESS_DENIED)
|
||||
|
||||
val json = JSON.toJSON(result)
|
||||
|
||||
// 返回响应
|
||||
response.contentType = "application/json;charset=UTF-8"
|
||||
response.writer.println(json)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package cn.bunny.services.security.handelr
|
||||
|
||||
import cn.bunny.common.service.utils.JwtHelper
|
||||
import cn.bunny.common.service.utils.ResponseUtil.Companion.out
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import cn.bunny.dao.pojo.result.ResultCodeEnum
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
|
||||
/**
|
||||
* 请求未认证接口
|
||||
*/
|
||||
class SecurityAuthenticationEntryPoint : AuthenticationEntryPoint {
|
||||
private val logger = LogManager.getLogger(SecurityAuthenticationEntryPoint::class.java)
|
||||
|
||||
override fun commence(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
authException: AuthenticationException
|
||||
) {
|
||||
val token = response.getHeader("token")
|
||||
val message = authException.message
|
||||
// 创建结果对象
|
||||
val result: Result<Any?> = when {
|
||||
token.isNullOrEmpty() -> Result.error(ResultCodeEnum.LOGIN_AUTH)
|
||||
|
||||
JwtHelper.isExpired(token) -> {
|
||||
logger.info("登录Token过期:{},用户id:{}", message, null)
|
||||
Result.error(ResultCodeEnum.AUTHENTICATION_EXPIRED)
|
||||
}
|
||||
|
||||
else -> {
|
||||
logger.info("请求未授权接口:{},用户id:{}", message, token)
|
||||
Result.error(ResultCodeEnum.LOGGED_IN_FROM_ANOTHER_DEVICE)
|
||||
}
|
||||
}
|
||||
|
||||
// 返回响应
|
||||
out(response, result)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cn.bunny.services.security.handelr
|
||||
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import com.alibaba.fastjson2.JSON
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import java.io.IOException
|
||||
|
||||
class SecurityAuthenticationFailureHandler : AuthenticationFailureHandler {
|
||||
@Throws(IOException::class, ServletException::class)
|
||||
override fun onAuthenticationFailure(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
exception: AuthenticationException
|
||||
) {
|
||||
// 错误消息
|
||||
val localizedMessage = exception.localizedMessage
|
||||
val result = Result.error(localizedMessage)
|
||||
|
||||
// 转成JSON
|
||||
val json = JSON.toJSON(result)
|
||||
|
||||
// 返回响应
|
||||
response.contentType = "application/json;charset=UTF-8"
|
||||
response.writer.println(json)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package cn.bunny.services.security.handelr
|
||||
|
||||
import cn.bunny.dao.pojo.result.Result
|
||||
import com.alibaba.fastjson2.JSON
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
class SecurityAuthenticationSuccessHandler : AuthenticationSuccessHandler {
|
||||
@Throws(IOException::class)
|
||||
override fun onAuthenticationSuccess(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
authentication: Authentication
|
||||
) {
|
||||
// 获取用户身份信息
|
||||
val principal = authentication.principal
|
||||
val result = Result.success(principal)
|
||||
|
||||
// 返回
|
||||
response.contentType = "application/json;charset=UTF-8"
|
||||
response.writer.println(JSON.toJSON(result))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package cn.bunny.services.security.service
|
||||
|
||||
import org.springframework.security.authorization.AuthorizationManager
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
||||
|
||||
interface CustomAuthorizationManagerService : AuthorizationManager<RequestAuthorizationContext?>
|
|
@ -0,0 +1,23 @@
|
|||
package cn.bunny.services.security.service
|
||||
|
||||
import cn.bunny.dao.dto.user.LoginDto
|
||||
import cn.bunny.dao.vo.user.LoginVo
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
|
||||
interface CustomUserDetailsService : UserDetailsService {
|
||||
/**
|
||||
* 根据用户名获取用户对象(获取不到直接抛异常)
|
||||
*/
|
||||
@Throws(UsernameNotFoundException::class)
|
||||
override fun loadUserByUsername(username: String): UserDetails
|
||||
|
||||
/**
|
||||
* 前台用户登录接口
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 登录后结果返回
|
||||
*/
|
||||
fun login(loginDto: LoginDto): LoginVo
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package cn.bunny.services.security.service.impl;
|
||||
|
||||
import cn.bunny.common.service.utils.JwtHelper;
|
||||
import cn.bunny.services.security.service.CustomAuthorizationManagerService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义权限判断
|
||||
* 判断用户有哪些权限
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CustomAuthorizationManagerServiceImpl implements CustomAuthorizationManagerService {
|
||||
|
||||
@Override
|
||||
public void verify(Supplier<Authentication> authentication, RequestAuthorizationContext requestAuthorizationContext) {
|
||||
CustomAuthorizationManagerService.super.verify(authentication, requestAuthorizationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
|
||||
// 用户的token和用户id、请求Url
|
||||
HttpServletRequest request = context.getRequest();
|
||||
String token = request.getHeader("token");
|
||||
// 用户id
|
||||
Long userId = JwtHelper.getUserId(token);
|
||||
// 请求地址
|
||||
String requestURI = request.getRequestURI();
|
||||
// 请求方式
|
||||
String method = request.getMethod();
|
||||
// 角色代码列表
|
||||
List<String> roleCodeList = authentication.get().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
|
||||
|
||||
return new AuthorizationDecision(hasRoleList(requestURI, method, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户所属的角色信息
|
||||
*
|
||||
* @param requestURI 请求url地址
|
||||
* @param method 请求方式
|
||||
* @param userId 用户id
|
||||
*/
|
||||
private Boolean hasRoleList(String requestURI, String method, Long userId) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package cn.bunny.services.security.service.impl
|
||||
|
||||
import cn.bunny.dao.dto.user.LoginDto
|
||||
import cn.bunny.dao.entity.user.AdminUser
|
||||
import cn.bunny.dao.vo.user.LoginVo
|
||||
import cn.bunny.services.mapper.UserMapper
|
||||
import cn.bunny.services.security.custom.CustomUser
|
||||
import cn.bunny.services.security.service.CustomUserDetailsService
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class CustomUserDetailsServiceImpl : CustomUserDetailsService {
|
||||
@Autowired
|
||||
private val userMapper: UserMapper? = null
|
||||
|
||||
@Throws(UsernameNotFoundException::class)
|
||||
override fun loadUserByUsername(username: String): UserDetails {
|
||||
// 根据邮箱查询用户名
|
||||
val user = userMapper!!.selectOne(
|
||||
Wrappers.lambdaQuery<AdminUser>().eq(SFunction<AdminUser, Any> { obj: AdminUser -> obj.email }, username)
|
||||
)
|
||||
// 都为空抛出异常,用户不存在
|
||||
if (user == null) {
|
||||
throw UsernameNotFoundException("")
|
||||
}
|
||||
|
||||
// 查询所有的角色
|
||||
return CustomUser(user, AuthorityUtils.createAuthorityList(listOf("admin", "common")))
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台用户登录接口
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 登录后结果返回
|
||||
*/
|
||||
override fun login(loginDto: LoginDto): LoginVo {
|
||||
// 自定义登录逻辑
|
||||
return LoginVo()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.bunny.services.service;
|
||||
|
||||
import cn.bunny.dao.entity.i18n.I18n;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
public interface I18nService extends IService<I18n> {
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.bunny.services.service;
|
||||
|
||||
import cn.bunny.dao.entity.i18n.I18nType;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言类型表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
public interface I18nTypeService extends IService<I18nType> {
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.bunny.services.service;
|
||||
|
||||
import cn.bunny.dao.entity.user.AdminUser;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户信息 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
public interface UserService extends IService<AdminUser> {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.services.service.impl;
|
||||
|
||||
import cn.bunny.dao.entity.i18n.I18n;
|
||||
import cn.bunny.services.mapper.I18nMapper;
|
||||
import cn.bunny.services.service.I18nService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Service
|
||||
public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I18nService {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.services.service.impl;
|
||||
|
||||
import cn.bunny.dao.entity.i18n.I18nType;
|
||||
import cn.bunny.services.mapper.I18nTypeMapper;
|
||||
import cn.bunny.services.service.I18nTypeService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 多语言类型表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Service
|
||||
public class I18nTypeServiceImpl extends ServiceImpl<I18nTypeMapper, I18nType> implements I18nTypeService {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.services.service.impl;
|
||||
|
||||
import cn.bunny.dao.entity.user.AdminUser;
|
||||
import cn.bunny.services.mapper.UserMapper;
|
||||
import cn.bunny.services.service.UserService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户信息 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author Bunny
|
||||
* @since 2024-09-26
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implements UserService {
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
bunny:
|
||||
datasource1:
|
||||
host: 192.168.3.98
|
||||
port: 3304
|
||||
sqlData: auth_admin
|
||||
username: root
|
||||
password: "02120212"
|
||||
datasource2:
|
||||
host: 106.15.251.123
|
||||
port: 3304
|
||||
sqlData: auth_admin_i18n
|
||||
username: root
|
||||
password: "02120212"
|
||||
|
||||
redis:
|
||||
host: 192.168.3.98
|
||||
port: 6379
|
||||
database: 0
|
||||
password: "123456"
|
||||
|
||||
minio:
|
||||
endpointUrl: "http://192.168.3.98:9000"
|
||||
accessKey: bunny
|
||||
secretKey: "02120212"
|
||||
bucket-name: bunny-bbs
|
|
@ -0,0 +1,25 @@
|
|||
bunny:
|
||||
datasource:
|
||||
host: 192.168.3.98
|
||||
port: 3304
|
||||
sqlData: bunny_docs
|
||||
username: root
|
||||
password: "02120212"
|
||||
datasource2:
|
||||
host: 192.168.3.98
|
||||
port: 3304
|
||||
sqlData: bunny_docs_i18n
|
||||
username: root
|
||||
password: "02120212"
|
||||
|
||||
redis:
|
||||
host: 192.168.3.98
|
||||
port: 6379
|
||||
database: 0
|
||||
password: "123456"
|
||||
|
||||
minio:
|
||||
endpointUrl: "http://192.168.3.98:9000"
|
||||
accessKey: bunny
|
||||
secretKey: "02120212"
|
||||
bucket-name: bunny-bbs
|
|
@ -0,0 +1,79 @@
|
|||
server:
|
||||
port: 8800
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
application:
|
||||
name: service-admin
|
||||
main:
|
||||
lazy-initialization: true
|
||||
jmx:
|
||||
enabled: false
|
||||
datasource:
|
||||
dynamic:
|
||||
primary: master #设置默认的数据源或者数据源组,默认值即为master
|
||||
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
|
||||
grace-destroy: false #是否优雅关闭数据源,默认为false,设置为true时,关闭数据源时如果数据源中还存在活跃连接,至多等待10s后强制关闭
|
||||
datasource:
|
||||
master:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://${bunny.datasource1.host}:${bunny.datasource1.port}/${bunny.datasource1.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
||||
username: ${bunny.datasource1.username}
|
||||
password: ${bunny.datasource1.password}
|
||||
i18n:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://${bunny.datasource2.host}:${bunny.datasource2.port}/${bunny.datasource2.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
||||
username: ${bunny.datasource2.username}
|
||||
password: ${bunny.datasource2.password}
|
||||
aop:
|
||||
enabled: true
|
||||
|
||||
data:
|
||||
redis:
|
||||
host: ${bunny.redis.host}
|
||||
port: ${bunny.redis.port}
|
||||
database: ${bunny.redis.database}
|
||||
password: ${bunny.redis.password}
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 20 #最大连接数
|
||||
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
|
||||
max-idle: 5 #最大空闲
|
||||
min-idle: 0 #最小空闲
|
||||
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: GMT+8
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 5MB
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
global-config:
|
||||
db-config:
|
||||
logic-delete-field: isDeleted
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
|
||||
|
||||
logging:
|
||||
level:
|
||||
cn.bunny.service.controller: info
|
||||
cn.bunny.service.service: info
|
||||
cn.bunny.service.mapper: info
|
||||
root: info
|
||||
|
||||
pattern:
|
||||
dateformat: HH:mm:ss:SSS
|
||||
file:
|
||||
path: "logs/${spring.application.name}"
|
||||
|
||||
bunny:
|
||||
minio:
|
||||
endpointUrl: ${bunny.minio.endpointUrl}
|
||||
accessKey: ${bunny.minio.accessKey}
|
||||
secretKey: ${bunny.minio.secretKey}
|
||||
bucket-name: ${bunny.minio.bucket-name}
|
|
@ -0,0 +1,6 @@
|
|||
__________ ____ __. __ .__ .__
|
||||
\______ \__ __ ____ ____ ___.__. | |/ _|_____/ |_| | |__| ____
|
||||
| | _/ | \/ \ / < | | | < / _ \ __\ | | |/ \
|
||||
| | \ | / | \ | \___ | | | ( <_> ) | | |_| | | \
|
||||
|______ /____/|___| /___| / ____| |____|__ \____/|__| |____/__|___| /
|
||||
\/ \/ \/\/ \/ \/
|
Binary file not shown.
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<configuration debug="false" xmlns="http://ch.qos.logback/xml/ns/logback"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
|
||||
https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
|
||||
|
||||
<appender name="STOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>
|
||||
%cyan([%thread]) %yellow(%-5level) %green(%logger{100}).%boldRed(%method)-%boldMagenta(%line) - %blue(%msg%n)
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- additivity:false 禁止重复打印日志 -->
|
||||
|
||||
<!-- 让SpringBoot内部日志ERROR级别 减少日志输出 -->
|
||||
<logger name="org.springframework" level="ERROR" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
<!-- 让mybatis整合包日志ERROR 减少日志输出 -->
|
||||
<logger name="org.mybatis" level="ERROR" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
<!-- 让ibatis 日志ERROR 减少日志输出 -->
|
||||
<logger name="org.apache.ibatis" level="ERROR" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
<!-- 让 tomcat包打印日志 日志ERROR 减少日志输出 -->
|
||||
<logger name="org.apache" level="ERROR" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
<!-- 我们自己开发的程序为DEBUG -->
|
||||
<logger name="com.redpig" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
|
||||
<logger name="com.baomidou" level="ERROR" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
<logger name="com.zaxxer" level="ERROR" additivity="false">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</logger>
|
||||
|
||||
<!-- Activiti日志 -->
|
||||
<logger name="org.activiti" level="ERROR" />
|
||||
<logger name="org.activiti.engine.impl.persistence.entity" level="DEBUG" />
|
||||
<logger name="_org.springframework" level="ERROR" />
|
||||
<logger name="springfox.documentation" level="ERROR" />
|
||||
|
||||
<!-- root级别开debug 子目录根据需要关闭 -->
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STOUT"/>
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,23 @@
|
|||
<?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="cn.bunny.services.mapper.I18nMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="cn.bunny.dao.entity.i18n.I18n">
|
||||
<id column="id" property="id"/>
|
||||
<result column="type_id" property="typeId"/>
|
||||
<result column="key_name" property="keyName"/>
|
||||
<result column="summary" property="summary"/>
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, type_id, key_name, summary, create_user, create_time, update_time, update_user, is_deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,23 @@
|
|||
<?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="cn.bunny.services.mapper.I18nTypeMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="cn.bunny.dao.entity.i18n.I18nType">
|
||||
<id column="id" property="id"/>
|
||||
<result column="language_name" property="languageName"/>
|
||||
<result column="summary" property="summary"/>
|
||||
<result column="is_default" property="isDefault"/>
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, language_name, summary, is_default, create_user, create_time, update_time, update_user, is_deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,31 @@
|
|||
<?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="cn.bunny.services.mapper.UserMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="cn.bunny.dao.entity.user.AdminUser">
|
||||
<id column="id" property="id"/>
|
||||
<result column="username" property="username"/>
|
||||
<result column="nick_name" property="nickName"/>
|
||||
<result column="email" property="email"/>
|
||||
<result column="phone" property="phone"/>
|
||||
<result column="password" property="password"/>
|
||||
<result column="avatar" property="avatar"/>
|
||||
<result column="sex" property="sex"/>
|
||||
<result column="summary" property="summary"/>
|
||||
<result column="last_login_ip" property="lastLoginIp"/>
|
||||
<result column="last_login_ip_address" property="lastLoginIpAddress"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, username, nick_name, email, phone, password, avatar, sex, summary, last_login_ip, last_login_ip_address, status, create_user, create_time, update_time, update_user, is_deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Loading…
Reference in New Issue