Change namespace to top.fatweb.oxygen.api

This commit is contained in:
2023-12-28 13:39:42 +08:00
parent 605f3f4152
commit 47baa06125
231 changed files with 698 additions and 682 deletions

View File

@@ -0,0 +1,67 @@
package top.fatweb.oxygen.api
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.transaction.annotation.EnableTransactionManagement
import java.io.File
import java.util.*
/**
* Application main class
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@SpringBootApplication
@EnableTransactionManagement
@EnableScheduling
class OxygenApiApplication
/**
* Main function
*
* @param args
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun main(args: Array<String>) {
val logger = LoggerFactory.getLogger("main")
if (!File("data").isDirectory) {
if (!File("data").mkdir()) {
logger.error("Can not create directory 'data', please try again later.")
return
}
}
if (!File("data/db").isDirectory) {
if (!File("data/db").mkdir()) {
logger.error("Can not create directory 'data/db', please try again later.")
return
}
}
if (!File("data/db/sqlite.db").isFile || File("data/db/sqlite.db").inputStream()
.use { it.readNBytes(15).toString(Charsets.UTF_8) != "SQLite format 3" }
) {
logger.warn("The 'data/db/sqlite.db' database is lost or damaged, recreating...")
if (File("data/db/sqlite.db").exists() && !File("data/db/sqlite.db").delete()) {
logger.error("Can not recreate database 'data/db/sqlite.db', please try again later.")
}
}
if (File("application-config.yml").exists() || File("data/application-config.yml").exists()) {
runApplication<OxygenApiApplication>(*args)
} else {
logger.warn("File 'application-config.yml' cannot be found in the running path or the data path. The configuration file template 'application-config.example.yml' has been created in directory 'data'. Please change the configuration file content, move it to the running path, rename it to 'application-config.yml', and then restart the server.")
OxygenApiApplication::class.java.getResource("/application-config-template.yml")?.readText()?.let {
File("data/application-config.example.yml").writeText(
it.replace(
"\$uuid\$", UUID.randomUUID().toString().replace("-", "")
)
)
}
}
}

View File

@@ -0,0 +1,32 @@
package top.fatweb.oxygen.api.annotation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.core.annotation.AliasFor
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
/**
* API controller annotation
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Tag
* @see RequestMapping
* @see RestController
*/
@Tag(name = "")
@RequestMapping
@RestController
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ApiController(
val version: Int = 1,
@get:AliasFor(annotation = RestController::class, attribute = "value") val value: String = "",
@get:AliasFor(annotation = RequestMapping::class, attribute = "path") val path: Array<String> = [""],
@get:AliasFor(annotation = Tag::class, attribute = "name") val name: String,
@get:AliasFor(annotation = Tag::class, attribute = "description") val description: String
)

View File

@@ -0,0 +1,27 @@
package top.fatweb.oxygen.api.annotation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.core.annotation.AliasFor
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
/**
* Base controller annotation
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RequestMapping
* @see RestController
*/
@Tag(name = "")
@RequestMapping
@RestController
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class BaseController(
@get:AliasFor(annotation = RequestMapping::class, attribute = "path") val path: Array<String> = [""],
@get:AliasFor(annotation = Tag::class, attribute = "name") val name: String,
@get:AliasFor(annotation = Tag::class, attribute = "description") val description: String
)

View File

@@ -0,0 +1,15 @@
package top.fatweb.oxygen.api.annotation
import top.fatweb.oxygen.api.entity.system.EventLog
/**
* Event log record annotation
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class EventLogRecord(
val event: EventLog.Event
)

View File

@@ -0,0 +1,24 @@
package top.fatweb.oxygen.api.annotation
import io.swagger.v3.oas.annotations.Hidden
import org.springframework.core.annotation.AliasFor
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
/**
* Hidden controller annotation
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Hidden
* @see RequestMapping
* @see RestController
*/
@Hidden
@RequestMapping
@RestController
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class HiddenController(
@get:AliasFor(annotation = RequestMapping::class, attribute = "path") val path: Array<String> = [""]
)

View File

@@ -0,0 +1,58 @@
package top.fatweb.oxygen.api.aop
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.stereotype.Component
import top.fatweb.oxygen.api.annotation.EventLogRecord
import top.fatweb.oxygen.api.service.system.IEventLogService
import top.fatweb.oxygen.api.util.WebUtil
import top.fatweb.oxygen.api.vo.permission.LoginVo
import top.fatweb.oxygen.api.vo.permission.RegisterVo
/**
* Event log record aspect
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IEventLogService
*/
@Aspect
@Component
class EventLogAspect(
private val eventLogService: IEventLogService
) {
/**
* Event log record pointcut
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Pointcut("@annotation(top.fatweb.oxygen.api.annotation.EventLogRecord)")
fun eventLogPointcut() {
}
/**
* Do after event log record pointcut
*
* @param joinPoint Join point
* @param retValue Return value
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see JoinPoint
*/
@AfterReturning(value = "eventLogPointcut()", returning = "retValue")
fun doAfter(joinPoint: JoinPoint, retValue: Any?) {
val annotation = (joinPoint.signature as MethodSignature).method.getAnnotation(EventLogRecord::class.java)
val userId = WebUtil.getLoginUserId() ?: when (retValue) {
is LoginVo -> retValue.userId!!
is RegisterVo -> retValue.userId!!
else -> -1
}
eventLogService.saveEvent(annotation, userId)
}
}

View File

@@ -0,0 +1,60 @@
package top.fatweb.oxygen.api.config
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer
import org.springframework.boot.jackson.JsonComponent
import org.springframework.context.annotation.Bean
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
/**
* Data format configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@JsonComponent
class DataFormatConfig {
/**
* The format of the time in response when request APIs
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@set:Value("\${spring.jackson.date-format}")
lateinit var dataFormat: String
/**
* The timezone of the time in response when request APIs
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see TimeZone
*/
@set:Value("\${spring.jackson.time-zone}}")
lateinit var timeZone: TimeZone
@Bean
fun jackson2ObjectMapperBuilder() = Jackson2ObjectMapperBuilderCustomizer { builder: Jackson2ObjectMapperBuilder ->
val tz = timeZone
val df: DateFormat = SimpleDateFormat(dataFormat)
df.timeZone = tz
builder.failOnEmptyBeans(false).failOnUnknownProperties(false)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).dateFormat(df)
}
@Bean
fun jackson2ObjectMapperBuilderCustomizer() =
Jackson2ObjectMapperBuilderCustomizer { builder: Jackson2ObjectMapperBuilder ->
builder.serializerByType(
LocalDateTime::class.java, LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dataFormat))
)
}
}

View File

@@ -0,0 +1,24 @@
package top.fatweb.oxygen.api.config
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import top.fatweb.oxygen.api.filter.ExceptionFilter
/**
* Filter configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Configuration
class FilterConfig {
@Bean
fun exceptionFilterRegistrationBean(exceptionFilter: ExceptionFilter): FilterRegistrationBean<ExceptionFilter> {
val registrationBean = FilterRegistrationBean(exceptionFilter)
registrationBean.setBeanName("exceptionFilter")
registrationBean.order = -100
return registrationBean
}
}

View File

@@ -0,0 +1,43 @@
package top.fatweb.oxygen.api.config
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource
import jakarta.annotation.PostConstruct
import org.flywaydb.core.Flyway
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.DependsOn
import top.fatweb.oxygen.api.properties.FlywayProperties
import javax.sql.DataSource
/**
* Flyway configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@DependsOn("flywayProperties")
@Configuration
class FlywayConfig(
private val dataSource: DataSource
) {
@PostConstruct
fun migrateOrder() {
val ds = dataSource as DynamicRoutingDataSource
ds.dataSources.forEach { (k: String, v: DataSource?) ->
val flyway = Flyway.configure()
.dataSource(v)
.locations(*FlywayProperties.locations.map { "$it/$k" }.toTypedArray())
.baselineOnMigrate(FlywayProperties.baselineOnMigrate)
.table(FlywayProperties.table)
.outOfOrder(FlywayProperties.outOfOrder)
.validateOnMigrate(FlywayProperties.validateOnMigrate)
.encoding(FlywayProperties.encoding)
.sqlMigrationPrefix(FlywayProperties.sqlMigrationPrefix)
.sqlMigrationSeparator(FlywayProperties.sqlMigrationSeparator)
.sqlMigrationSuffixes(*FlywayProperties.sqlMigrationSuffixes.toTypedArray())
.baselineVersion(FlywayProperties.baselineVersion)
.load()
flyway.migrate()
}
}
}

View File

@@ -0,0 +1,69 @@
package top.fatweb.oxygen.api.config
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import jakarta.annotation.PostConstruct
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.DependsOn
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Component
import top.fatweb.avatargenerator.GitHubAvatar
import top.fatweb.oxygen.api.entity.permission.User
import top.fatweb.oxygen.api.entity.permission.UserInfo
import top.fatweb.oxygen.api.properties.AdminProperties
import top.fatweb.oxygen.api.service.permission.IUserInfoService
import top.fatweb.oxygen.api.service.permission.IUserService
import top.fatweb.oxygen.api.util.StrUtil
/**
* Application initialization configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IUserService
* @see IUserInfoService
* @see PasswordEncoder
*/
@DependsOn("adminProperties")
@Component
class InitConfig(
private val userService: IUserService,
private val userInfoService: IUserInfoService,
private val passwordEncoder: PasswordEncoder
) {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
@PostConstruct
fun init() {
if (!userService.exists(KtQueryWrapper(User()).eq(User::id, 0))) {
userInfoService.remove(KtQueryWrapper(UserInfo()).eq(UserInfo::userId, 0))
val rawPassword = AdminProperties.password ?: let {
logger.warn("No default administrator password is set, a randomly generated password will be used")
StrUtil.getRandomPassword(10)
}
val encodedPassword = passwordEncoder.encode(rawPassword)
val user = User().apply {
id = 0
username = AdminProperties.username
password = encodedPassword
locking = 0
enable = 1
}
val userInfo = UserInfo().apply {
userId = 0
nickname = AdminProperties.nickname
avatar =
GitHubAvatar.newAvatarBuilder().build().createAsBase64((Long.MIN_VALUE..Long.MAX_VALUE).random())
email = AdminProperties.email
}
if (userService.save(user) && userInfoService.save(userInfo)) {
logger.warn("First startup, create administrator - username: admin, password: $rawPassword")
logger.warn("This information will only be shown once. Please change your password promptly after logging in.")
}
}
}
}

View File

@@ -0,0 +1,25 @@
package top.fatweb.oxygen.api.config
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
/**
* Mybatis-plus configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Configuration
class MybatisPlusConfig {
@Bean
fun mybatisPlusInterceptor(): MybatisPlusInterceptor {
val mybatisPlusInterceptor = MybatisPlusInterceptor()
mybatisPlusInterceptor.addInnerInterceptor(OptimisticLockerInnerInterceptor())
mybatisPlusInterceptor.addInnerInterceptor(PaginationInnerInterceptor())
return mybatisPlusInterceptor
}
}

View File

@@ -0,0 +1,48 @@
package top.fatweb.oxygen.api.config
import com.fasterxml.jackson.annotation.JsonAutoDetect
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.annotation.PropertyAccessor
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer
/**
* Redis configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Configuration
class RedisConfig {
@Bean
fun redisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<*, *> {
val redisTemplate = RedisTemplate<String, Any>()
redisTemplate.connectionFactory = redisConnectionFactory
val stringRedisSerializer = StringRedisSerializer()
val objectMapper = ObjectMapper().registerModules(JavaTimeModule()).apply {
setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
activateDefaultTyping(
this.polymorphicTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY
)
}
val anyJackson2JsonRedisSerializer = Jackson2JsonRedisSerializer(objectMapper, Any::class.java)
// 使用StringRedisSerializer来序列化和反序列化redis的key值
redisTemplate.keySerializer = stringRedisSerializer
redisTemplate.valueSerializer = anyJackson2JsonRedisSerializer
// Hash的key也采用StringRedisSerializer的序列化方式
redisTemplate.hashKeySerializer = stringRedisSerializer
redisTemplate.hashValueSerializer = anyJackson2JsonRedisSerializer
redisTemplate.afterPropertiesSet()
return redisTemplate
}
}

View File

@@ -0,0 +1,104 @@
package top.fatweb.oxygen.api.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
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.configurers.*
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import top.fatweb.oxygen.api.filter.JwtAuthenticationTokenFilter
import top.fatweb.oxygen.api.handler.JwtAccessDeniedHandler
import top.fatweb.oxygen.api.handler.JwtAuthenticationEntryPointHandler
/**
* Spring Security configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see JwtAuthenticationTokenFilter
* @see JwtAuthenticationEntryPointHandler
* @see JwtAccessDeniedHandler
*/
@Configuration
@EnableMethodSecurity
class SecurityConfig(
private val jwtAuthenticationTokenFilter: JwtAuthenticationTokenFilter,
private val authenticationEntryPointHandler: JwtAuthenticationEntryPointHandler,
private val accessDeniedHandler: JwtAccessDeniedHandler
) {
@Bean
fun passwordEncoder() = BCryptPasswordEncoder()
@Bean
fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager =
authenticationConfiguration.authenticationManager
@Bean
fun corsConfigurationSource(): UrlBasedCorsConfigurationSource {
val corsConfiguration = CorsConfiguration()
corsConfiguration.allowedMethods = listOf("*")
corsConfiguration.allowedHeaders = listOf("*")
corsConfiguration.maxAge = 3600L
corsConfiguration.allowedOrigins = listOf("*")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", corsConfiguration)
return source
}
@Bean
fun securityFilterChain(httpSecurity: HttpSecurity): SecurityFilterChain = httpSecurity
// Disable CSRF
.csrf { csrfConfigurer: CsrfConfigurer<HttpSecurity> -> csrfConfigurer.disable() }
// Do not get SecurityContent by Session
.sessionManagement { sessionManagementConfigurer: SessionManagementConfigurer<HttpSecurity?> ->
sessionManagementConfigurer.sessionCreationPolicy(
SessionCreationPolicy.STATELESS
)
}
.authorizeHttpRequests { authorizeHttpRequests: AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry ->
authorizeHttpRequests
// Allow anonymous access
.requestMatchers(
"/error/thrown",
"/doc.html",
"/swagger-ui/**",
"/webjars/**",
"/v3/**",
"/swagger-ui.html",
"/favicon.ico",
"/login",
"/register",
"/forget",
"/retrieve"
).anonymous()
// Authentication required
.anyRequest().authenticated()
}
.logout { logoutConfigurer: LogoutConfigurer<HttpSecurity> -> logoutConfigurer.disable() }
.exceptionHandling { exceptionHandlingConfigurer: ExceptionHandlingConfigurer<HttpSecurity?> ->
exceptionHandlingConfigurer.authenticationEntryPoint(
authenticationEntryPointHandler
)
exceptionHandlingConfigurer.accessDeniedHandler(
accessDeniedHandler
)
}
.cors { cors: CorsConfigurer<HttpSecurity?> ->
cors.configurationSource(
corsConfigurationSource()
)
}
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter::class.java).build()
}

View File

@@ -0,0 +1,28 @@
package top.fatweb.oxygen.api.config
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Contact
import io.swagger.v3.oas.models.info.Info
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import top.fatweb.oxygen.api.properties.ServerProperties
/**
* Swagger API doc configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Configuration
class SwaggerConfig {
@Bean
fun customOpenAPI(): OpenAPI? {
val contact = Contact().name("FatttSnake").url("https://fatweb.top").email("fatttsnake@gmail.com")
return OpenAPI().info(
Info().title("Oxygen API 文档").description("Oxygen 后端 API 文档,包含各个 Controller 调用信息")
.contact(contact).version(
ServerProperties.version
)
)
}
}

View File

@@ -0,0 +1,24 @@
package top.fatweb.oxygen.api.config
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import top.fatweb.oxygen.api.interceptor.SysLogInterceptor
/**
* System log configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see SysLogInterceptor
* @see WebMvcConfigurer
*/
@Configuration
class SysLogConfig(
private val sysLogInterceptor: SysLogInterceptor
) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(sysLogInterceptor).addPathPatterns("/**")
.excludePathPatterns("/error/thrown", "/webjars/**")
}
}

View File

@@ -0,0 +1,23 @@
package top.fatweb.oxygen.api.config
import org.apache.velocity.app.VelocityEngine
import org.apache.velocity.runtime.RuntimeConstants
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
/**
* Velocity engine configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Configuration
class VelocityEngineConfig {
@Bean
fun velocityEngine() = VelocityEngine().apply {
setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath")
setProperty("classpath.resource.loader.class", ClasspathResourceLoader::class.java.name)
init()
}
}

View File

@@ -0,0 +1,21 @@
package top.fatweb.oxygen.api.config
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import top.fatweb.oxygen.api.annotation.ApiController
/**
* Web MVC configurer configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see WebMvcRegistrations
*/
@Configuration
class WebMvcConfigurerConfig : WebMvcConfigurer {
override fun configurePathMatch(configurer: PathMatchConfigurer) {
configurer.addPathPrefix("/api/{API_VERSION}") { it.isAnnotationPresent(ApiController::class.java) }
}
}

View File

@@ -0,0 +1,18 @@
package top.fatweb.oxygen.api.config
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
import top.fatweb.oxygen.api.util.ApiResponseMappingHandlerMapping
/**
* Web MVC registrations configuration
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see WebMvcRegistrations
*/
@Configuration
class WebMvcRegistrationsConfig : WebMvcRegistrations {
override fun getRequestMappingHandlerMapping(): RequestMappingHandlerMapping = ApiResponseMappingHandlerMapping()
}

View File

@@ -0,0 +1,19 @@
package top.fatweb.oxygen.api.controller
import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.RequestMapping
import top.fatweb.oxygen.api.annotation.HiddenController
/**
* Exception controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@HiddenController(["/error"])
class ExceptionController {
@RequestMapping("/thrown")
fun thrown(request: HttpServletRequest) {
throw request.getAttribute("filter.error") as RuntimeException
}
}

View File

@@ -0,0 +1,204 @@
package top.fatweb.oxygen.api.controller.api.v1
import io.swagger.v3.oas.annotations.Operation
import jakarta.validation.Valid
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import top.fatweb.oxygen.api.annotation.ApiController
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarBaseParam
import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarGitHubParam
import top.fatweb.oxygen.api.service.api.v1.IAvatarService
import top.fatweb.oxygen.api.vo.api.v1.avatar.AvatarBase64Vo
/**
* Avatar controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IAvatarService
*/
@ApiController(value = "avatarControllerV1", path = ["/avatar"], name = "随机头像 V1", description = "随机头像相关接口")
class AvatarController(
private val avatarService: IAvatarService
) {
/**
* Get random avatar
*
* @param avatarBaseParam Avatar base parameters
* @return Avatar byte array
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ByteArray
*/
@Operation(summary = "获取随机头像")
@GetMapping(produces = [MediaType.IMAGE_PNG_VALUE])
fun getRandom(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray =
avatarService.random(avatarBaseParam)
/**
* Get random avatar as base64
*
* @param avatarBaseParam Avatar base parameters
* @return Response object includes avatar base64 string
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ResponseResult
* @see AvatarBase64Vo
*/
@Operation(summary = "获取随机头像 Base64")
@GetMapping("base64")
fun getRandomBase64(
@Valid avatarBaseParam: AvatarBaseParam?
): ResponseResult<AvatarBase64Vo> =
ResponseResult.success(
ResponseCode.API_AVATAR_SUCCESS, data = avatarService.randomBase64(avatarBaseParam)
)
/**
* Get triangle avatar
*
* @param avatarBaseParam Avatar base parameters
* @return Avatar byte array
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ByteArray
*/
@Operation(summary = "三角形头像")
@GetMapping("/triangle", produces = [MediaType.IMAGE_PNG_VALUE])
fun triangle(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray =
avatarService.triangle(avatarBaseParam)
/**
* Get triangle avatar as base64
*
* @param avatarBaseParam Avatar base parameters
* @return Response object includes avatar base64 string
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ResponseResult
* @see AvatarBase64Vo
*/
@Operation(summary = "三角形头像 Base64")
@GetMapping("/triangle/base64")
fun triangleBase64(
@Valid avatarBaseParam: AvatarBaseParam?
): ResponseResult<AvatarBase64Vo> =
ResponseResult.success(
ResponseCode.API_AVATAR_SUCCESS,
data = avatarService.triangleBase64(avatarBaseParam)
)
/**
* Get square avatar
*
* @param avatarBaseParam Avatar base parameters
* @return Avatar byte array
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ByteArray
*/
@Operation(summary = "正方形头像")
@GetMapping("/square", produces = [MediaType.IMAGE_PNG_VALUE])
fun square(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray =
avatarService.square(avatarBaseParam)
/**
* Get square avatar as base64
*
* @param avatarBaseParam Avatar base parameters
* @return Response object includes avatar base64 string
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ResponseResult
* @see AvatarBase64Vo
*/
@Operation(summary = "正方形头像 Base64")
@GetMapping("/square/base64")
fun squareBase64(
@Valid avatarBaseParam: AvatarBaseParam?
): ResponseResult<AvatarBase64Vo> =
ResponseResult.success(
ResponseCode.API_AVATAR_SUCCESS,
data = avatarService.squareBase64(avatarBaseParam)
)
/**
* Get identicon avatar
*
* @param avatarBaseParam Avatar base parameters
* @return Avatar byte array
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ByteArray
*/
@Operation(summary = "Identicon 头像")
@GetMapping("/identicon", produces = [MediaType.IMAGE_PNG_VALUE])
fun identicon(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray =
avatarService.identicon(avatarBaseParam)
/**
* Get identicon avatar as base64
*
* @param avatarBaseParam Avatar base parameters
* @return Response object includes avatar base64 string
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarBaseParam
* @see ResponseResult
* @see AvatarBase64Vo
*/
@Operation(summary = "Identicon 头像 Base64")
@GetMapping("/identicon/base64")
fun identiconBase64(
@Valid avatarBaseParam: AvatarBaseParam?
): ResponseResult<AvatarBase64Vo> =
ResponseResult.success(
ResponseCode.API_AVATAR_SUCCESS,
data = avatarService.identiconBase64(avatarBaseParam)
)
/**
* Get GitHub avatar
*
* @param avatarGitHubParam Avatar base parameters
* @return Avatar byte array
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarGitHubParam
* @see ByteArray
*/
@Operation(summary = "GitHub 头像")
@GetMapping("/github", produces = [MediaType.IMAGE_PNG_VALUE])
fun github(@Valid avatarGitHubParam: AvatarGitHubParam?): ByteArray =
avatarService.github(avatarGitHubParam)
/**
* Get GitHub avatar as base64
*
* @param avatarGitHubParam Avatar base parameters
* @return Response object includes avatar base64 string
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AvatarGitHubParam
* @see ResponseResult
* @see AvatarBase64Vo
*/
@Operation(summary = "GitHub 头像 Base64")
@GetMapping("/github/base64")
fun githubBase64(
@Valid avatarGitHubParam: AvatarGitHubParam?
): ResponseResult<AvatarBase64Vo> =
ResponseResult.success(
ResponseCode.API_AVATAR_SUCCESS,
data = avatarService.githubBase64(avatarGitHubParam)
)
}

View File

@@ -0,0 +1,184 @@
package top.fatweb.oxygen.api.controller.permission
import io.swagger.v3.oas.annotations.Operation
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.permission.*
import top.fatweb.oxygen.api.service.permission.IAuthenticationService
import top.fatweb.oxygen.api.util.WebUtil
import top.fatweb.oxygen.api.vo.permission.LoginVo
import top.fatweb.oxygen.api.vo.permission.RegisterVo
import top.fatweb.oxygen.api.vo.permission.TokenVo
/**
* Authentication controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IAuthenticationService
*/
@BaseController(name = "身份认证", description = "身份认证相关接口")
class AuthenticationController(
private val authenticationService: IAuthenticationService
) {
/**
* Register
*
* @param registerParam Register parameters
* @return Response object includes user ID
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RegisterParam
* @see ResponseResult
* @see RegisterVo
*/
@Operation(summary = "注册")
@PostMapping("/register")
fun register(@Valid @RequestBody registerParam: RegisterParam): ResponseResult<RegisterVo> = ResponseResult.success(
ResponseCode.PERMISSION_REGISTER_SUCCESS,
data = authenticationService.register(registerParam)
)
/**
* Send verify email
*
* @return Response object includes resend result
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
*/
@Operation(summary = "发送验证邮件")
@PostMapping("/resend")
fun resend(): ResponseResult<Nothing> {
authenticationService.resend()
return ResponseResult.success(ResponseCode.PERMISSION_RESEND_SUCCESS)
}
/**
* Verify email
*
* @param verifyParam Verify parameters
* @return Response object includes verify result
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see VerifyParam
* @see ResponseResult
*/
@Operation(summary = "验证邮箱")
@PostMapping("/verify")
fun verify(@Valid @RequestBody verifyParam: VerifyParam): ResponseResult<Nothing> {
authenticationService.verify(verifyParam)
return ResponseResult.success(ResponseCode.PERMISSION_VERIFY_SUCCESS)
}
/**
* Forget password
*
* @param request
* @param forgetParam Forget parameters
* @return Response object includes forget result
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see HttpServletRequest
* @see ForgetParam
* @see ResponseResult
*/
@Operation(summary = "忘记密码")
@PostMapping("/forget")
fun forget(request: HttpServletRequest, @Valid @RequestBody forgetParam: ForgetParam): ResponseResult<Nothing> {
authenticationService.forget(request, forgetParam)
return ResponseResult.success(ResponseCode.PERMISSION_FORGET_SUCCESS)
}
/**
* Retrieve password
*
* @param request
* @param retrieveParam Retrieve parameters
* @return Response object include retrieve result
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see HttpServletRequest
* @see RetrieveParam
* @see ResponseResult
*/
@Operation(summary = "找回密码")
@PostMapping("/retrieve")
fun retrieve(
request: HttpServletRequest,
@Valid @RequestBody retrieveParam: RetrieveParam
): ResponseResult<Nothing> {
authenticationService.retrieve(request, retrieveParam)
return ResponseResult.success(ResponseCode.PERMISSION_RETRIEVE_SUCCESS)
}
/**
* Login
*
* @param request
* @param loginParam Login parameters
* @return Response object includes login result
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see HttpServletRequest
* @see LoginParam
* @see ResponseResult
* @see LoginVo
*/
@Operation(summary = "登录")
@PostMapping("/login")
fun login(request: HttpServletRequest, @Valid @RequestBody loginParam: LoginParam): ResponseResult<LoginVo> =
ResponseResult.success(
ResponseCode.PERMISSION_LOGIN_SUCCESS,
"Login success",
authenticationService.login(request, loginParam)
)
/**
* Logout
*
* @param request
* @return Response object includes logout result
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see HttpServletRequest
* @see ResponseResult
*/
@Operation(summary = "登出")
@PostMapping("/logout")
fun logout(request: HttpServletRequest): ResponseResult<Nothing> =
when (authenticationService.logout(WebUtil.getToken(request))) {
true -> ResponseResult.success(ResponseCode.PERMISSION_LOGOUT_SUCCESS, "Logout success", null)
false -> ResponseResult.fail(ResponseCode.PERMISSION_LOGOUT_FAILED, "Logout failed", null)
}
/**
* Renew token
*
* @param request
* @return Response object includes new token
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see HttpServletRequest
* @see ResponseResult
* @see TokenVo
*/
@Operation(summary = "更新 Token")
@GetMapping("/token")
fun renewToken(request: HttpServletRequest): ResponseResult<TokenVo> = ResponseResult.success(
ResponseCode.PERMISSION_TOKEN_RENEW_SUCCESS,
"Token renew success",
authenticationService.renewToken(WebUtil.getToken(request))
)
}

View File

@@ -0,0 +1,178 @@
package top.fatweb.oxygen.api.controller.permission
import io.swagger.v3.oas.annotations.Operation
import jakarta.validation.Valid
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.permission.group.*
import top.fatweb.oxygen.api.service.permission.IGroupService
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.permission.GroupWithRoleVo
import top.fatweb.oxygen.api.vo.permission.base.GroupVo
/**
* Group controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IGroupService
*/
@BaseController(path = ["/system/group"], name = "用户组管理", description = "用户组管理相关接口")
class GroupController(
val groupService: IGroupService
) {
/**
* Get group by ID
*
* @param id Group ID
* @return Response object includes group info
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see GroupWithRoleVo
*/
@Operation(summary = "获取单个用户组")
@GetMapping("/{id}")
@PreAuthorize("hasAnyAuthority('system:group:query:one')")
fun getOne(@PathVariable id: Long): ResponseResult<GroupWithRoleVo> =
ResponseResult.databaseSuccess(
data = groupService.getOne(id)
)
/**
* Get group paging information
*
* @param groupGetParam Get group parameters
* @return Response object includes group paging information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupGetParam
* @see ResponseResult
* @see PageVo
* @see GroupWithRoleVo
*/
@Operation(summary = "获取用户组")
@GetMapping
@PreAuthorize("hasAnyAuthority('system:group:query:all')")
fun get(@Valid groupGetParam: GroupGetParam?): ResponseResult<PageVo<GroupWithRoleVo>> =
ResponseResult.databaseSuccess(
data = groupService.getPage(groupGetParam)
)
/**
* Get group list
*
* @return Response object includes group list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see GroupVo
*/
@Operation(summary = "获取用户组列表")
@GetMapping("/list")
@PreAuthorize("hasAnyAuthority('system:group:query:list', 'system:user:add:one', 'system:user:modify:one')")
fun list(): ResponseResult<List<GroupVo>> =
ResponseResult.databaseSuccess(
data = groupService.listAll()
)
/**
* Add group
*
* @param groupAddParam Add group parameters
* @return Response object includes group information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupAddParam
* @see ResponseResult
* @see GroupVo
*/
@Operation(summary = "添加用户组")
@PostMapping
@PreAuthorize("hasAnyAuthority('system:group:add:one')")
fun add(@Valid @RequestBody groupAddParam: GroupAddParam): ResponseResult<GroupVo> =
groupService.add(groupAddParam)?.let {
ResponseResult.databaseSuccess(
ResponseCode.DATABASE_INSERT_SUCCESS, data = it
)
} ?: let { ResponseResult.databaseFail(ResponseCode.DATABASE_INSERT_FAILED) }
/**
* Update group
*
* @param groupUpdateParam Update group parameters
* @return Response object includes group information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupUpdateParam
* @see ResponseResult
* @see GroupVo
*/
@Operation(summary = "修改用户组")
@PutMapping
@PreAuthorize("hasAnyAuthority('system:group:modify:one')")
fun update(@Valid @RequestBody groupUpdateParam: GroupUpdateParam): ResponseResult<GroupVo> =
groupService.update(groupUpdateParam)?.let {
ResponseResult.databaseSuccess(
ResponseCode.DATABASE_UPDATE_SUCCESS, data = it
)
} ?: let { ResponseResult.databaseFail(ResponseCode.DATABASE_UPDATE_FILED) }
/**
* Update status of group
*
* @param groupUpdateStatusParam Update status of group parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupUpdateStatusParam
* @see ResponseResult
*/
@Operation(summary = "修改用户组状态")
@PatchMapping
@PreAuthorize("hasAnyAuthority('system:group:modify:status')")
fun updateStatus(@Valid @RequestBody groupUpdateStatusParam: GroupUpdateStatusParam): ResponseResult<Nothing> =
if (groupService.status(groupUpdateStatusParam)) {
ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS)
} else {
ResponseResult.databaseFail(ResponseCode.DATABASE_UPDATE_FILED)
}
/**
* Delete group by ID
*
* @param id Group ID
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
*/
@Operation(summary = "删除用户组")
@DeleteMapping("/{id}")
@PreAuthorize("hasAnyAuthority('system:group:delete:one')")
fun delete(@PathVariable id: Long): ResponseResult<Nothing> {
groupService.deleteOne(id)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS)
}
/**
* Delete group by list
*
* @param groupDeleteParam Delete group parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupDeleteParam
* @see ResponseResult
*/
@Operation(summary = "批量删除用户组")
@DeleteMapping
@PreAuthorize("hasAnyAuthority('system:group:delete:multiple')")
fun deleteList(@Valid @RequestBody groupDeleteParam: GroupDeleteParam): ResponseResult<Nothing> {
groupService.delete(groupDeleteParam)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS)
}
}

View File

@@ -0,0 +1,34 @@
package top.fatweb.oxygen.api.controller.permission
import io.swagger.v3.oas.annotations.Operation
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.service.permission.IPowerService
import top.fatweb.oxygen.api.vo.permission.PowerSetVo
/**
* Power controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPowerService
*/
@BaseController(path = ["/system/power"], name = "权限管理", description = "权限管理相关接口")
class PowerController(
private val powerService: IPowerService
) {
/**
* Get power list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see PowerSetVo
*/
@Operation(summary = "获取权限列表")
@GetMapping("/list")
@PreAuthorize("hasAnyAuthority('system:power:query:list', 'system:role:add:one', 'system:role:modify:one')")
fun getList(): ResponseResult<PowerSetVo> = ResponseResult.databaseSuccess(data = powerService.getList())
}

View File

@@ -0,0 +1,183 @@
package top.fatweb.oxygen.api.controller.permission
import io.swagger.v3.oas.annotations.Operation
import jakarta.validation.Valid
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.permission.role.*
import top.fatweb.oxygen.api.service.permission.IRoleService
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.permission.RoleWithPowerVo
import top.fatweb.oxygen.api.vo.permission.base.RoleVo
/**
* Role controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IRoleService
*/
@BaseController(path = ["/system/role"], name = "角色管理", description = "角色管理相关接口")
class RoleController(
private val roleService: IRoleService
) {
/**
* Get role by ID
*
* @param id Role ID
* @return Response object includes role information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see RoleWithPowerVo
*/
@Operation(summary = "获取单个角色")
@GetMapping("/{id}")
@PreAuthorize("hasAnyAuthority('system:role:query:one')")
fun getOne(@PathVariable id: Long): ResponseResult<RoleWithPowerVo> {
return ResponseResult.databaseSuccess(
data = roleService.getOne(id)
)
}
/**
* Get role paging information
*
* @param roleGetParam Get role parameters
* @return Response object includes role paging information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleGetParam
* @see ResponseResult
* @see RoleWithPowerVo
*/
@Operation(summary = "获取角色")
@GetMapping
@PreAuthorize("hasAnyAuthority('system:role:query:all')")
fun get(roleGetParam: RoleGetParam?): ResponseResult<PageVo<RoleWithPowerVo>> {
return ResponseResult.databaseSuccess(
data = roleService.getPage(roleGetParam)
)
}
/**
* Get role list
*
* @return Response object includes role list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see RoleVo
*/
@Operation(summary = "获取角色列表")
@GetMapping("/list")
@PreAuthorize("hasAnyAuthority('system:role:query:list', 'system:group:add:one', 'system:group:modify:one', 'system:user:add:one', 'system:user:modify:one')")
fun list(): ResponseResult<List<RoleVo>> {
return ResponseResult.databaseSuccess(
data = roleService.listAll()
)
}
/**
* Add role
*
* @param roleAddParam Add role parameters
* @return Response object includes role information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleAddParam
* @see ResponseResult
* @see RoleVo
*/
@Operation(summary = "添加角色")
@PostMapping
@PreAuthorize("hasAnyAuthority('system:role:add:one')")
fun add(@Valid @RequestBody roleAddParam: RoleAddParam): ResponseResult<RoleVo> {
return roleService.add(roleAddParam)?.let {
ResponseResult.databaseSuccess(
ResponseCode.DATABASE_INSERT_SUCCESS, data = it
)
} ?: let { ResponseResult.databaseFail(ResponseCode.DATABASE_INSERT_FAILED) }
}
/**
* Update role
*
* @param roleUpdateParam Update role parameters
* @return Response object includes role information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleUpdateParam
* @see ResponseResult
* @see RoleVo
*/
@Operation(summary = "修改角色")
@PutMapping
@PreAuthorize("hasAnyAuthority('system:role:modify:one')")
fun update(@Valid @RequestBody roleUpdateParam: RoleUpdateParam): ResponseResult<RoleVo> {
return roleService.update(roleUpdateParam)?.let {
ResponseResult.databaseSuccess(
ResponseCode.DATABASE_UPDATE_SUCCESS, data = it
)
} ?: let { ResponseResult.databaseFail(ResponseCode.DATABASE_UPDATE_FILED) }
}
/**
* Update status of role
*
* @param roleUpdateStatusParam Update status of role parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleUpdateStatusParam
* @see ResponseResult
*/
@Operation(summary = "修改角色状态")
@PatchMapping
@PreAuthorize("hasAnyAuthority('system:role:modify:status')")
fun status(@Valid @RequestBody roleUpdateStatusParam: RoleUpdateStatusParam): ResponseResult<Nothing> {
return if (roleService.status(roleUpdateStatusParam)) {
ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS)
} else {
ResponseResult.databaseFail(ResponseCode.DATABASE_UPDATE_FILED)
}
}
/**
* Delete role by ID
*
* @param id
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
*/
@Operation(summary = "删除角色")
@DeleteMapping("/{id}")
@PreAuthorize("hasAnyAuthority('system:role:delete:one')")
fun delete(@PathVariable id: Long): ResponseResult<Nothing> {
roleService.deleteOne(id)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS)
}
/**
* Delete role by list
*
* @param roleDeleteParam Delete role parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleDeleteParam
* @see ResponseResult
*/
@Operation(summary = "批量删除角色")
@DeleteMapping
@PreAuthorize("hasAnyAuthority('system:role:delete:multiple')")
fun deleteList(@Valid @RequestBody roleDeleteParam: RoleDeleteParam): ResponseResult<Nothing> {
roleService.delete(roleDeleteParam)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS)
}
}

View File

@@ -0,0 +1,178 @@
package top.fatweb.oxygen.api.controller.permission
import io.swagger.v3.oas.annotations.Operation
import jakarta.validation.Valid
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.exception.NoRecordFoundException
import top.fatweb.oxygen.api.param.permission.user.*
import top.fatweb.oxygen.api.service.permission.IUserService
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.permission.UserWithPasswordRoleInfoVo
import top.fatweb.oxygen.api.vo.permission.UserWithPowerInfoVo
import top.fatweb.oxygen.api.vo.permission.UserWithRoleInfoVo
/**
* User controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IUserService
*/
@BaseController(path = ["/system/user"], name = "用户管理", description = "用户管理相关接口")
class UserController(
private val userService: IUserService
) {
/**
* Get current user information
*
* @return Response object includes user information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see UserWithPowerInfoVo
*/
@Operation(summary = "获取当前用户信息")
@GetMapping("info")
fun getInfo(): ResponseResult<UserWithPowerInfoVo> =
userService.getInfo()?.let {
ResponseResult.databaseSuccess(data = it)
} ?: let { ResponseResult.databaseFail() }
/**
* Get user by ID
*
* @param id User ID
* @return Response object includes user information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see UserWithRoleInfoVo
*/
@Operation(summary = "获取单个用户")
@GetMapping("/{id}")
@PreAuthorize("hasAnyAuthority('system:user:query:one')")
fun getOne(@PathVariable id: Long): ResponseResult<UserWithRoleInfoVo> =
userService.getOne(id)?.let {
ResponseResult.databaseSuccess(data = it)
} ?: let {
throw NoRecordFoundException()
}
/**
* Get user paging information
*
* @param userGetParam Get user parameters
* @return Response object includes user paging information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserGetParam
* @see ResponseResult
* @see UserWithRoleInfoVo
*/
@Operation(summary = "获取用户")
@GetMapping
@PreAuthorize("hasAnyAuthority('system:user:query:all')")
fun get(@Valid userGetParam: UserGetParam?): ResponseResult<PageVo<UserWithRoleInfoVo>> =
ResponseResult.databaseSuccess(
data = userService.getPage(userGetParam)
)
/**
* Add user
*
* @param userAddParam Add user parameters
* @return Response object includes user information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserAddParam
* @see ResponseResult
* @see UserWithPasswordRoleInfoVo
*/
@Operation(summary = "添加用户")
@PostMapping
@PreAuthorize("hasAnyAuthority('system:user:add:one')")
fun add(@Valid @RequestBody userAddParam: UserAddParam): ResponseResult<UserWithPasswordRoleInfoVo> =
userService.add(userAddParam)?.let {
ResponseResult.databaseSuccess(
ResponseCode.DATABASE_INSERT_SUCCESS, data = it
)
} ?: let { ResponseResult.databaseFail(ResponseCode.DATABASE_INSERT_FAILED) }
/**
* Update user
*
* @param userUpdateParam Update user parameters
* @return Response object includes user information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserUpdateParam
* @see ResponseResult
* @see UserWithRoleInfoVo
*/
@Operation(summary = "修改用户")
@PutMapping
@PreAuthorize("hasAnyAuthority('system:user:modify:one')")
fun update(@Valid @RequestBody userUpdateParam: UserUpdateParam): ResponseResult<UserWithRoleInfoVo> =
userService.update(userUpdateParam)?.let {
ResponseResult.databaseSuccess(
ResponseCode.DATABASE_UPDATE_SUCCESS, data = it
)
} ?: let { ResponseResult.databaseFail(ResponseCode.DATABASE_UPDATE_FILED) }
/**
* Update user password
*
* @param userUpdatePasswordParam Update user password parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserUpdatePasswordParam
* @see ResponseResult
*/
@Operation(summary = "修改密码")
@PatchMapping
@PreAuthorize("hasAnyAuthority('system:user:modify:password')")
fun password(@Valid @RequestBody userUpdatePasswordParam: UserUpdatePasswordParam): ResponseResult<Nothing> {
userService.password(userUpdatePasswordParam)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS)
}
/**
* Delete user by ID
*
* @param id User ID
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
*/
@Operation(summary = "删除用户")
@DeleteMapping("/{id}")
@PreAuthorize("hasAnyAuthority('system:user:delete:one')")
fun delete(@PathVariable id: Long): ResponseResult<Nothing> {
userService.deleteOne(id)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS)
}
/**
* Delete user by list
*
* @param userDeleteParam Delete user parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserDeleteParam
* @see ResponseResult
*/
@Operation(summary = "批量删除用户")
@DeleteMapping
@PreAuthorize("hasAnyAuthority('system:user:delete:multiple')")
fun deleteList(@Valid @RequestBody userDeleteParam: UserDeleteParam): ResponseResult<Nothing> {
userService.delete(userDeleteParam)
return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS)
}
}

View File

@@ -0,0 +1,100 @@
package top.fatweb.oxygen.api.controller.system
import io.swagger.v3.oas.annotations.Operation
import jakarta.validation.Valid
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.system.BaseSettingsParam
import top.fatweb.oxygen.api.param.system.MailSendParam
import top.fatweb.oxygen.api.param.system.MailSettingsParam
import top.fatweb.oxygen.api.service.system.ISettingsService
import top.fatweb.oxygen.api.vo.system.BaseSettingsVo
import top.fatweb.oxygen.api.vo.system.MailSettingsVo
/**
* System settings controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ISettingsService
*/
@BaseController(path = ["/system/settings"], name = "系统设置", description = "系统设置相关接口")
class SettingsController(
private val settingsService: ISettingsService
) {
/**
* Get base settings
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "获取基础设置")
@GetMapping("/base")
@PreAuthorize("hasAnyAuthority('system:settings:query:base')")
fun getApp(): ResponseResult<BaseSettingsVo> = ResponseResult.success(data = settingsService.getBase())
/**
* Update base settings
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "更新基础设置")
@PutMapping("/base")
@PreAuthorize("hasAnyAuthority('system:settings:modify:base')")
fun updateApp(@RequestBody baseSettingsParam: BaseSettingsParam): ResponseResult<Nothing> {
settingsService.updateBase(baseSettingsParam)
return ResponseResult.success()
}
/**
* Get mail settings
*
* @return Response object includes mail settings
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see MailSettingsVo
*/
@Operation(summary = "获取邮件设置")
@GetMapping("/mail")
@PreAuthorize("hasAnyAuthority('system:settings:query:mail')")
fun getMail(): ResponseResult<MailSettingsVo> = ResponseResult.success(data = settingsService.getMail())
/**
* Update mail settings
*
* @param mailSettingsParam Mail settings parameters
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see MailSettingsParam
* @see ResponseResult
*/
@Operation(summary = "更新邮件设置")
@PutMapping("/mail")
@PreAuthorize("hasAnyAuthority('system:settings:modify:mail')")
fun updateMail(@RequestBody mailSettingsParam: MailSettingsParam): ResponseResult<Nothing> {
settingsService.updateMail(mailSettingsParam)
return ResponseResult.success()
}
/**
* Send mail test
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "邮件发送测试")
@PostMapping("/mail")
@PreAuthorize("hasAnyAuthority('system:settings:modify:mail')")
fun sendMail(@RequestBody @Valid mailSendParam: MailSendParam): ResponseResult<Nothing> {
settingsService.sendMail(mailSendParam)
return ResponseResult.success()
}
}

View File

@@ -0,0 +1,103 @@
package top.fatweb.oxygen.api.controller.system
import io.swagger.v3.oas.annotations.Operation
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.system.ActiveInfoGetParam
import top.fatweb.oxygen.api.param.system.OnlineInfoGetParam
import top.fatweb.oxygen.api.service.system.IStatisticsService
import top.fatweb.oxygen.api.vo.system.*
/**
* Statistics controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IStatisticsService
*/
@BaseController(path = ["/system/statistics"], name = "统计接口", description = "系统信息统计相关接口")
class StatisticsController(
private val statisticService: IStatisticsService
) {
/**
* Get software information
*
* @return Response object includes software information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see SoftwareInfoVo
*/
@Operation(summary = "获取软件信息")
@GetMapping("/software")
@PreAuthorize("hasAnyAuthority('system:statistics:query:base')")
fun software(): ResponseResult<SoftwareInfoVo> = ResponseResult.success(data = statisticService.software())
/**
* Get hardware information
*
* @return Response object includes hardware information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see HardwareInfoVo
*/
@Operation(summary = "获取硬件信息")
@GetMapping("/hardware")
@PreAuthorize("hasAnyAuthority('system:statistics:query:base')")
fun hardware(): ResponseResult<HardwareInfoVo> = ResponseResult.success(data = statisticService.hardware())
/**
* Get CPU information
*
* @return Response object includes CPU information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see CpuInfoVo
*/
@Operation(summary = "获取 CPU 信息")
@GetMapping("/cpu")
@PreAuthorize("hasAnyAuthority('system:statistics:query:real')")
fun cpu(): ResponseResult<CpuInfoVo> = ResponseResult.success(data = statisticService.cpu())
/**
* Get storage information
*
* @return Response object includes storage information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseResult
* @see StorageInfoVo
*/
@Operation(summary = "获取存储信息")
@GetMapping("/storage")
@PreAuthorize("hasAnyAuthority('system:statistics:query:real')")
fun storage(): ResponseResult<StorageInfoVo> = ResponseResult.success(data = statisticService.storage())
/**
* Get the history of online users information
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "获取在线用户数量信息")
@GetMapping("/online")
@PreAuthorize("hasAnyAuthority('system:statistics:query:usage')")
fun online(onlineInfoGetParam: OnlineInfoGetParam?): ResponseResult<OnlineInfoVo> =
ResponseResult.success(data = statisticService.online(onlineInfoGetParam))
/**
* Get the history of active information
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "获取用户活跃信息")
@GetMapping("/active")
@PreAuthorize("hasAnyAuthority('system:statistics:query:usage')")
fun active(activeInfoGetParam: ActiveInfoGetParam): ResponseResult<ActiveInfoVo> =
ResponseResult.success(data = statisticService.active(activeInfoGetParam))
}

View File

@@ -0,0 +1,45 @@
package top.fatweb.oxygen.api.controller.system
import io.swagger.v3.oas.annotations.Operation
import jakarta.validation.Valid
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import top.fatweb.oxygen.api.annotation.BaseController
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.param.system.SysLogGetParam
import top.fatweb.oxygen.api.service.system.ISysLogService
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.system.SysLogVo
/**
* System log controller
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ISysLogService
*/
@BaseController(path = ["/system/log"], name = "系统日志", description = "系统日志相关接口")
class SysLogController(
private val sysLogService: ISysLogService
) {
/**
* Get system log
*
* @param sysLogGetParam Get system log parameters
* @return Response object includes system log
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see SysLogGetParam
* @see ResponseResult
* @see SysLogVo
*/
@Operation(summary = "获取")
@GetMapping
@PreAuthorize("hasAnyAuthority('system:log:query:all')")
fun get(@Valid sysLogGetParam: SysLogGetParam?): ResponseResult<PageVo<SysLogVo>> {
return ResponseResult.success(
ResponseCode.DATABASE_SELECT_SUCCESS, data = sysLogService.getPage(sysLogGetParam)
)
}
}

View File

@@ -0,0 +1,29 @@
package top.fatweb.oxygen.api.converter.permission
import top.fatweb.oxygen.api.entity.permission.Func
import top.fatweb.oxygen.api.vo.permission.base.FuncVo
/**
* Function converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object FuncConverter {
/**
* Convert Func object into FuncVo object
*
* @param func Func object
* @return FuncVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Func
* @see FuncVo
*/
fun funcToFuncVo(func: Func) = FuncVo(
id = func.id,
name = func.name,
parentId = func.parentId,
menuId = func.menuId
)
}

View File

@@ -0,0 +1,126 @@
package top.fatweb.oxygen.api.converter.permission
import com.baomidou.mybatisplus.core.metadata.IPage
import top.fatweb.oxygen.api.entity.permission.Group
import top.fatweb.oxygen.api.entity.permission.Role
import top.fatweb.oxygen.api.param.permission.group.GroupAddParam
import top.fatweb.oxygen.api.param.permission.group.GroupUpdateParam
import top.fatweb.oxygen.api.param.permission.group.GroupUpdateStatusParam
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.permission.GroupWithRoleVo
import top.fatweb.oxygen.api.vo.permission.base.GroupVo
/**
* Group converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object GroupConverter {
/**
* Convert Group object into GroupVo object
*
* @param group Group object
* @return GroupVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Group
* @see GroupVo
*/
fun groupToGroupVo(group: Group) = GroupVo(
id = group.id,
name = group.name,
enable = group.enable == 1,
createTime = group.createTime,
updateTime = group.updateTime
)
/**
* Convert Group object into GroupWithRoleVo object
*
* @param group Group object
* @return GroupWithRoleVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Group
* @see GroupWithRoleVo
*/
fun groupToGroupWithRoleVo(group: Group) = GroupWithRoleVo(
id = group.id,
name = group.name,
enable = group.enable == 1,
createTime = group.createTime,
updateTime = group.updateTime,
roles = group.roles?.map { RoleConverter.roleToRoleVo(it) }
)
/**
* Convert IPage<Group> object into PageVo<GroupWithRoleVo> object
*
* @param groupPage IPage<Group> object
* @return PageVo<GroupWithRoleVo> object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
* @see Group
* @see PageVo
* @see GroupWithRoleVo
*/
fun groupPageToGroupWithRolePageVo(groupPage: IPage<Group>) = PageVo(
total = groupPage.total,
pages = groupPage.pages,
size = groupPage.size,
current = groupPage.current,
records = groupPage.records.map {
groupToGroupWithRoleVo(it)
}
)
/**
* Convert GroupAddParam object into Group object
*
* @param groupAddParam GroupAddParam object
* @return Group object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupAddParam
* @see Group
*/
fun groupAddParamToGroup(groupAddParam: GroupAddParam) = Group().apply {
name = groupAddParam.name
enable = if (groupAddParam.enable) 1 else 0
roles = groupAddParam.roleIds?.map { Role().apply { id = it } }
}
/**
* Convert GroupUpdateParam object into Group object
*
* @param groupUpdateParam GroupUpdateParam object
* @return Group object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupUpdateParam
* @see Group
*/
fun groupUpdateParamToGroup(groupUpdateParam: GroupUpdateParam) = Group().apply {
id = groupUpdateParam.id
name = groupUpdateParam.name
enable = if (groupUpdateParam.enable) 1 else 0
roles = groupUpdateParam.roleIds?.map { Role().apply { id = it } }
}
/**
* Convert GroupUpdateStatusParam object into Group object
*
* @param groupUpdateStatusParam GroupUpdateStatusParam object
* @return Group object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see GroupUpdateStatusParam
* @see Group
*/
fun groupUpdateStatusParamToGroup(groupUpdateStatusParam: GroupUpdateStatusParam) = Group().apply {
id = groupUpdateStatusParam.id
enable = if (groupUpdateStatusParam.enable) 1 else 0
}
}

View File

@@ -0,0 +1,30 @@
package top.fatweb.oxygen.api.converter.permission
import top.fatweb.oxygen.api.entity.permission.Menu
import top.fatweb.oxygen.api.vo.permission.base.MenuVo
/**
* Menu converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object MenuConverter {
/**
* Convert Menu object into MenuVo object
*
* @param menu Menu object
* @return MenuVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Menu
* @see MenuVo
*/
fun menuToMenuVo(menu: Menu) = MenuVo(
id = menu.id,
name = menu.name,
url = menu.url,
parentId = menu.parentId,
moduleId = menu.moduleId
)
}

View File

@@ -0,0 +1,27 @@
package top.fatweb.oxygen.api.converter.permission
import top.fatweb.oxygen.api.entity.permission.Module
import top.fatweb.oxygen.api.vo.permission.base.ModuleVo
/**
* Module converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object ModuleConverter {
/**
* Convert Module object into ModuleVo object
*
* @param module Module object
* @return ModuleVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Module
* @see ModuleVo
*/
fun moduleToModuleVo(module: Module) = ModuleVo(
id = module.id,
name = module.name
)
}

View File

@@ -0,0 +1,29 @@
package top.fatweb.oxygen.api.converter.permission
import top.fatweb.oxygen.api.entity.permission.Operation
import top.fatweb.oxygen.api.vo.permission.base.OperationVo
/**
* Operation converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object OperationConverter {
/**
* Convert Operation object into OperationVo object
*
* @param operation Operation object
* @return OperationVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Operation
* @see OperationVo
*/
fun operationToOperationVo(operation: Operation) = OperationVo(
id = operation.id,
name = operation.name,
code = operation.code,
funcId = operation.funcId
)
}

View File

@@ -0,0 +1,29 @@
package top.fatweb.oxygen.api.converter.permission
import top.fatweb.oxygen.api.entity.permission.PowerSet
import top.fatweb.oxygen.api.vo.permission.PowerSetVo
/**
* Power converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object PowerConverter {
/**
* Convert PowerSet object into PowerSetVo object
*
* @param powerSet PowerSet object
* @return PowerSetVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see PowerSet
* @see PowerSetVo
*/
fun powerSetToPowerSetVo(powerSet: PowerSet) = PowerSetVo(
moduleList = powerSet.moduleList?.map { ModuleConverter.moduleToModuleVo(it) },
menuList = powerSet.menuList?.map { MenuConverter.menuToMenuVo(it) },
funcList = powerSet.funcList?.map { FuncConverter.funcToFuncVo(it) },
operationList = powerSet.operationList?.map { OperationConverter.operationToOperationVo(it) }
)
}

View File

@@ -0,0 +1,129 @@
package top.fatweb.oxygen.api.converter.permission
import com.baomidou.mybatisplus.core.metadata.IPage
import top.fatweb.oxygen.api.entity.permission.Power
import top.fatweb.oxygen.api.entity.permission.Role
import top.fatweb.oxygen.api.param.permission.role.RoleAddParam
import top.fatweb.oxygen.api.param.permission.role.RoleUpdateParam
import top.fatweb.oxygen.api.param.permission.role.RoleUpdateStatusParam
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.permission.RoleWithPowerVo
import top.fatweb.oxygen.api.vo.permission.base.RoleVo
/**
* Role converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object RoleConverter {
/**
* Convert Role object into RoleVo object
*
* @param role Role object
* @return RoleVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Role
* @see RoleVo
*/
fun roleToRoleVo(role: Role) = RoleVo(
id = role.id,
name = role.name,
enable = role.enable == 1,
createTime = role.createTime,
updateTime = role.updateTime
)
/**
* Convert Role object into RoleWithPowerVo object
*
* @param role Role object
* @return RoleWithPowerVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Role
* @see RoleWithPowerVo
*/
fun roleToRoleWithPowerVo(role: Role) = RoleWithPowerVo(
id = role.id,
name = role.name,
enable = role.enable == 1,
createTime = role.createTime,
updateTime = role.updateTime,
modules = role.modules?.map { ModuleConverter.moduleToModuleVo(it) },
menus = role.menus?.map { MenuConverter.menuToMenuVo(it) },
funcs = role.funcs?.map { FuncConverter.funcToFuncVo(it) },
operations = role.operations?.map { OperationConverter.operationToOperationVo(it) }
)
/**
* Convert IPage<Role> object into PageVo object
*
* @param rolePage IPage<Role> object
* @return PageVo<RoleWithPowerVo> object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
* @see Role
* @see PageVo
* @see RoleWithPowerVo
*/
fun rolePageToRoleWithPowerPageVo(rolePage: IPage<Role>) = PageVo(
total = rolePage.total,
pages = rolePage.pages,
size = rolePage.size,
current = rolePage.current,
records = rolePage.records.map {
roleToRoleWithPowerVo(it)
}
)
/**
* Convert RoleAddParam object into Role object
*
* @param roleAddParam RoleAddParam object
* @return Role object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleAddParam
* @see Role
*/
fun roleAddParamToRole(roleAddParam: RoleAddParam) = Role().apply {
name = roleAddParam.name
enable = if (roleAddParam.enable) 1 else 0
powers = roleAddParam.powerIds?.map { Power().apply { id = it } }
}
/**
* Convert RoleUpdateParam into Role object
*
* @param roleUpdateParam RoleUpdateParam object
* @return Role object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleUpdateParam
* @see Role
*/
fun roleUpdateParamToRole(roleUpdateParam: RoleUpdateParam) = Role().apply {
id = roleUpdateParam.id
name = roleUpdateParam.name
enable = if (roleUpdateParam.enable) 1 else 0
powers = roleUpdateParam.powerIds?.map { Power().apply { id = it } }
}
/**
* Convert RoleUpdateStatusParam object into Role object
*
* @param roleUpdateStatusParam RoleUpdateStatusParam object
* @return Role object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RoleUpdateStatusParam
* @see Role
*/
fun roleUpdateStatusParamToRole(roleUpdateStatusParam: RoleUpdateStatusParam) = Role().apply {
id = roleUpdateStatusParam.id
enable = if (roleUpdateStatusParam.enable) 1 else 0
}
}

View File

@@ -0,0 +1,239 @@
package top.fatweb.oxygen.api.converter.permission
import com.baomidou.mybatisplus.core.metadata.IPage
import top.fatweb.avatargenerator.GitHubAvatar
import top.fatweb.oxygen.api.entity.permission.Group
import top.fatweb.oxygen.api.entity.permission.Role
import top.fatweb.oxygen.api.entity.permission.User
import top.fatweb.oxygen.api.entity.permission.UserInfo
import top.fatweb.oxygen.api.param.permission.user.UserAddParam
import top.fatweb.oxygen.api.param.permission.user.UserUpdateParam
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.permission.UserWithInfoVo
import top.fatweb.oxygen.api.vo.permission.UserWithPasswordRoleInfoVo
import top.fatweb.oxygen.api.vo.permission.UserWithPowerInfoVo
import top.fatweb.oxygen.api.vo.permission.UserWithRoleInfoVo
/**
* User converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object UserConverter {
/**
* Convert User object into UserWithPowerInfoVo object
*
* @param user User object
* @return UserWithPowerInfoVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
* @see UserWithPowerInfoVo
*/
fun userToUserWithPowerInfoVo(user: User) = UserWithPowerInfoVo(
id = user.id,
username = user.username,
verified = user.verify.isNullOrBlank(),
locking = user.locking?.let { it == 1 },
expiration = user.expiration,
credentialsExpiration = user.credentialsExpiration,
enable = user.enable?.let { it == 1 },
currentLoginTime = user.currentLoginTime,
currentLoginIp = user.currentLoginIp,
lastLoginTime = user.lastLoginTime,
lastLoginIp = user.lastLoginIp,
createTime = user.createTime,
updateTime = user.updateTime,
userInfo = user.userInfo?.let {
UserInfoConverter.userInfoToUserInfoVo(it)
},
modules = user.modules?.map {
ModuleConverter.moduleToModuleVo(it)
},
menus = user.menus?.map {
MenuConverter.menuToMenuVo(it)
},
funcs = user.funcs?.map {
FuncConverter.funcToFuncVo(it)
},
operations = user.operations?.map {
OperationConverter.operationToOperationVo(it)
}
)
/**
* Convert User object into UserWithRoleInfoVo object
*
* @param user User object
* @return UserWithRoleInfoVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
* @see UserWithRoleInfoVo
*/
fun userToUserWithRoleInfoVo(user: User) = UserWithRoleInfoVo(
id = user.id,
username = user.username,
verify = user.verify,
locking = user.locking?.let { it == 1 },
expiration = user.expiration,
credentialsExpiration = user.credentialsExpiration,
enable = user.enable?.let { it == 1 },
currentLoginTime = user.currentLoginTime,
currentLoginIp = user.currentLoginIp,
lastLoginTime = user.lastLoginTime,
lastLoginIp = user.lastLoginIp,
createTime = user.createTime,
updateTime = user.updateTime,
userInfo = user.userInfo?.let {
UserInfoConverter.userInfoToUserInfoVo(it)
},
roles = user.roles?.map {
RoleConverter.roleToRoleVo(it)
},
groups = user.groups?.map {
GroupConverter.groupToGroupVo(it)
}
)
/**
* Convert User object into UserWithInfoVo object
*
* @param user User object
* @return UserWithInfoVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
* @see UserWithInfoVo
*/
fun userToUserWithInfoVo(user: User) = UserWithInfoVo(
id = user.id,
username = user.username,
verified = user.verify.isNullOrBlank(),
locking = user.locking?.let { it == 1 },
expiration = user.expiration,
credentialsExpiration = user.credentialsExpiration,
enable = user.enable?.let { it == 1 },
currentLoginTime = user.currentLoginTime,
currentLoginIp = user.currentLoginIp,
lastLoginTime = user.lastLoginTime,
lastLoginIp = user.lastLoginIp,
createTime = user.createTime,
updateTime = user.updateTime,
userInfo = user.userInfo?.let {
UserInfoConverter.userInfoToUserInfoVo(it)
}
)
/**
* Convert User object into UserWithPasswordRoleInfoVo object
*
* @param user User object
* @return UserWithPasswordRoleInfoVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
* @see UserWithPasswordRoleInfoVo
*/
fun userToUserWithPasswordRoleInfoVo(user: User) = UserWithPasswordRoleInfoVo(
id = user.id,
username = user.username,
password = user.password,
verify = user.verify,
locking = user.locking?.let { it == 1 },
expiration = user.expiration,
credentialsExpiration = user.credentialsExpiration,
enable = user.enable?.let { it == 1 },
currentLoginTime = user.currentLoginTime,
currentLoginIp = user.currentLoginIp,
lastLoginTime = user.lastLoginTime,
lastLoginIp = user.lastLoginIp,
createTime = user.createTime,
updateTime = user.updateTime,
userInfo = user.userInfo?.let {
UserInfoConverter.userInfoToUserInfoVo(it)
},
roles = user.roles?.map {
RoleConverter.roleToRoleVo(it)
},
groups = user.groups?.map {
GroupConverter.groupToGroupVo(it)
}
)
/**
* Convert UserAddParam object into User object
*
* @param userAddParam UserAddParam object
* @return User object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserAddParam
* @see User
*/
fun userAddParamToUser(userAddParam: UserAddParam) = User().apply {
username = userAddParam.username
password = userAddParam.password
locking = if (userAddParam.locking) 1 else 0
expiration = userAddParam.expiration
credentialsExpiration = userAddParam.credentialsExpiration
enable = if (userAddParam.enable) 1 else 0
userInfo = UserInfo().apply {
nickname = userAddParam.nickname ?: userAddParam.username
avatar = userAddParam.avatar ?: GitHubAvatar.newAvatarBuilder().build()
.createAsBase64((Long.MIN_VALUE..Long.MAX_VALUE).random())
email = userAddParam.email
}
roles = userAddParam.roleIds?.map { Role().apply { id = it } }
groups = userAddParam.groupIds?.map { Group().apply { id = it } }
}
/**
* Convert UserUpdateParam object into User object
*
* @param userUpdateParam UserUpdateParam object
* @return User object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserUpdateParam
* @see User
*/
fun userUpdateParamToUser(userUpdateParam: UserUpdateParam) = User().apply {
id = userUpdateParam.id
username = userUpdateParam.username
locking = if (userUpdateParam.locking && userUpdateParam.id != 0L) 1 else 0
expiration = if (userUpdateParam.id != 0L) userUpdateParam.expiration else null
credentialsExpiration = userUpdateParam.credentialsExpiration
enable = if (userUpdateParam.enable || userUpdateParam.id == 0L) 1 else 0
userInfo = UserInfo().apply {
nickname = userUpdateParam.nickname
avatar = userUpdateParam.avatar
email = userUpdateParam.email
}
roles = if (userUpdateParam.id != 0L) userUpdateParam.roleIds?.map { Role().apply { id = it } } else null
groups = if (userUpdateParam.id != 0L) userUpdateParam.groupIds?.map { Group().apply { id = it } } else null
}
/**
* Convert IPage<User> object into PageVo<UserWithRoleInfoVo> object
*
* @param userPage IPage<User> object
* @return PageVo<UserWithRoleInfoVo> object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
* @see User
* @see PageVo
* @see UserWithRoleInfoVo
*/
fun userPageToUserWithRoleInfoPageVo(userPage: IPage<User>) = PageVo(
total = userPage.total,
pages = userPage.pages,
size = userPage.size,
current = userPage.current,
records = userPage.records.map {
userToUserWithRoleInfoVo(it)
}
)
}

View File

@@ -0,0 +1,32 @@
package top.fatweb.oxygen.api.converter.permission
import top.fatweb.oxygen.api.entity.permission.UserInfo
import top.fatweb.oxygen.api.vo.permission.base.UserInfoVo
/**
* User information converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object UserInfoConverter {
/**
* Convert UserInfo object into UserInfoVo object
*
* @param userInfo UserInfo object
* @return UserInfoVo object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserInfo
* @see UserInfoVo
*/
fun userInfoToUserInfoVo(userInfo: UserInfo) = UserInfoVo(
id = userInfo.id,
userId = userInfo.userId,
nickname = userInfo.nickname,
avatar = userInfo.avatar,
email = userInfo.email,
createTime = userInfo.createTime,
updateTime = userInfo.updateTime
)
}

View File

@@ -0,0 +1,53 @@
package top.fatweb.oxygen.api.converter.system
import com.baomidou.mybatisplus.core.metadata.IPage
import top.fatweb.oxygen.api.entity.system.SysLog
import top.fatweb.oxygen.api.vo.PageVo
import top.fatweb.oxygen.api.vo.system.SysLogVo
/**
* System log converter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
object SysLogConverter {
/**
* Convert IPage<SysLog> object into PageVo<SysLogVo> object
*
* @param syslogPage IPage<Syslog> object
* @return PageVo<SysLogVo> object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
* @see SysLog
* @see PageVo
* @see SysLogVo
*/
fun sysLogPageToSysLogPageVo(syslogPage: IPage<SysLog>) = PageVo(
syslogPage.total,
syslogPage.pages,
syslogPage.size,
syslogPage.current,
syslogPage.records.map {
SysLogVo(
id = it.id,
logType = it.logType,
operateUserId = it.operateUserId,
operateTime = it.operateTime,
requestUri = it.requestUri,
requestMethod = it.requestMethod,
requestParams = it.requestParams,
requestIp = it.requestIp,
requestServerAddress = it.requestServerAddress,
exception = it.exception == 1,
exceptionInfo = it.exceptionInfo,
startTime = it.startTime,
endTime = it.endTime,
executeTime = it.executeTime,
userAgent = it.userAgent,
operateUsername = it.operateUsername
)
})
}

View File

@@ -0,0 +1,35 @@
package top.fatweb.oxygen.api.cron
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import top.fatweb.oxygen.api.entity.system.StatisticsLog
import top.fatweb.oxygen.api.properties.SecurityProperties
import top.fatweb.oxygen.api.service.system.IStatisticsLogService
import top.fatweb.oxygen.api.util.RedisUtil
/**
* Statistics scheduled tasks
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Component
class StatisticsCron(
private val redisUtil: RedisUtil,
private val statisticsLogService: IStatisticsLogService
) {
/**
* Auto record number of online users
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Scheduled(cron = "0 * * * * *")
fun onlineUserCount() {
statisticsLogService.save(StatisticsLog().apply {
key = StatisticsLog.KeyItem.ONLINE_USERS_COUNT
value = redisUtil.keys("${SecurityProperties.jwtIssuer}_login_*")
.distinctBy { Regex("${SecurityProperties.jwtIssuer}_login_(.*):.*").matchEntire(it)?.groupValues?.getOrNull(1) }.size.toString()
})
}
}

View File

@@ -0,0 +1,41 @@
package top.fatweb.oxygen.api.entity.common
/**
* Business code entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
enum class BusinessCode(val code: Int) {
/**
* System
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
SYSTEM(100),
/**
* Permission
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
PERMISSION(200),
/**
* Database
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
DATABASE(300),
/**
* Avatar API
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
API_AVATAR(501)
}

View File

@@ -0,0 +1,62 @@
package top.fatweb.oxygen.api.entity.common
/**
* Response code entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
enum class ResponseCode(val code: Int) {
SYSTEM_OK(BusinessCode.SYSTEM, 0),
SYSTEM_ERROR(BusinessCode.SYSTEM, 50),
SYSTEM_TIMEOUT(BusinessCode.SYSTEM, 51),
SYSTEM_REQUEST_ILLEGAL(BusinessCode.SYSTEM, 52),
SYSTEM_ARGUMENT_NOT_VALID(BusinessCode.SYSTEM, 53),
PERMISSION_LOGIN_SUCCESS(BusinessCode.PERMISSION, 0),
PERMISSION_PASSWORD_CHANGE_SUCCESS(BusinessCode.PERMISSION, 1),
PERMISSION_LOGOUT_SUCCESS(BusinessCode.PERMISSION, 2),
PERMISSION_TOKEN_RENEW_SUCCESS(BusinessCode.PERMISSION, 3),
PERMISSION_REGISTER_SUCCESS(BusinessCode.PERMISSION, 4),
PERMISSION_RESEND_SUCCESS(BusinessCode.PERMISSION, 5),
PERMISSION_VERIFY_SUCCESS(BusinessCode.PERMISSION, 6),
PERMISSION_FORGET_SUCCESS(BusinessCode.PERMISSION, 7),
PERMISSION_RETRIEVE_SUCCESS(BusinessCode.PERMISSION, 8),
PERMISSION_UNAUTHORIZED(BusinessCode.PERMISSION, 50),
PERMISSION_USERNAME_NOT_FOUND(BusinessCode.PERMISSION, 51),
PERMISSION_ACCESS_DENIED(BusinessCode.PERMISSION, 52),
PERMISSION_USER_LOCKED(BusinessCode.PERMISSION, 53),
PERMISSION_USER_EXPIRED(BusinessCode.PERMISSION, 54),
PERMISSION_USER_CREDENTIALS_EXPIRED(BusinessCode.PERMISSION, 55),
PERMISSION_USER_DISABLE(BusinessCode.PERMISSION, 56),
PERMISSION_LOGIN_USERNAME_PASSWORD_ERROR(BusinessCode.PERMISSION, 57),
PERMISSION_OLD_PASSWORD_NOT_MATCH(BusinessCode.PERMISSION, 58),
PERMISSION_LOGOUT_FAILED(BusinessCode.PERMISSION, 59),
PERMISSION_TOKEN_ILLEGAL(BusinessCode.PERMISSION, 60),
PERMISSION_TOKEN_HAS_EXPIRED(BusinessCode.PERMISSION, 61),
PERMISSION_NO_VERIFICATION_REQUIRED(BusinessCode.PERMISSION, 62),
PERMISSION_VERIFY_CODE_ERROR_OR_EXPIRED(BusinessCode.PERMISSION, 63),
PERMISSION_ACCOUNT_NEED_INIT(BusinessCode.PERMISSION, 64),
PERMISSION_USER_NOT_FOUND(BusinessCode.PERMISSION, 65),
PERMISSION_RETRIEVE_CODE_ERROR_OR_EXPIRED(BusinessCode.PERMISSION, 66),
PERMISSION_ACCOUNT_NEED_RESET_PASSWORD(BusinessCode.PERMISSION, 67),
DATABASE_SELECT_SUCCESS(BusinessCode.DATABASE, 0),
DATABASE_SELECT_FAILED(BusinessCode.DATABASE, 5),
DATABASE_INSERT_SUCCESS(BusinessCode.DATABASE, 10),
DATABASE_INSERT_FAILED(BusinessCode.DATABASE, 15),
DATABASE_UPDATE_SUCCESS(BusinessCode.DATABASE, 20),
DATABASE_UPDATE_FILED(BusinessCode.DATABASE, 25),
DATABASE_DELETE_SUCCESS(BusinessCode.DATABASE, 30),
DATABASE_DELETE_FILED(BusinessCode.DATABASE, 35),
DATABASE_EXECUTE_ERROR(BusinessCode.DATABASE, 50),
DATABASE_DUPLICATE_KEY(BusinessCode.DATABASE, 51),
DATABASE_NO_RECORD_FOUND(BusinessCode.DATABASE, 52),
API_AVATAR_SUCCESS(BusinessCode.API_AVATAR, 0),
API_AVATAR_ERROR(BusinessCode.API_AVATAR, 50);
constructor(businessCode: BusinessCode, code: Int) : this(businessCode.code * 100 + code)
}

View File

@@ -0,0 +1,103 @@
package top.fatweb.oxygen.api.entity.common
import io.swagger.v3.oas.annotations.media.Schema
import java.io.Serializable
/**
* Response result entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
class ResponseResult<T> private constructor(
@Schema(description = "响应码", defaultValue = "200") val code: Int,
@Schema(description = "是否调用成功") val success: Boolean,
@Schema(description = "信息") val msg: String,
@Schema(description = "数据") val data: T?
) : Serializable {
companion object {
/**
* Build response result object
*
* @param code Response code
* @param success Is successful
* @param msg Response message
* @param data Response data
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun <T> build(code: Int, success: Boolean, msg: String, data: T?) =
ResponseResult(code, success, msg, data)
/**
* Build response result object
*
* @param code Response code object
* @param success Is successful
* @param msg Response message
* @param data Response data
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseCode
*/
fun <T> build(code: ResponseCode, success: Boolean, msg: String, data: T?) =
build(code.code, success, msg, data)
/**
* Build successful response result object
*
* @param code Response code object
* @param msg Response message
* @param data Response data
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseCode
*/
fun <T> success(code: ResponseCode = ResponseCode.SYSTEM_OK, msg: String = "success", data: T? = null) =
build(code, true, msg, data)
/**
* Build failure response result object
*
* @param code Response code object
* @param msg Response message
* @param data Response data
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseCode
*/
fun <T> fail(code: ResponseCode = ResponseCode.SYSTEM_ERROR, msg: String = "fail", data: T? = null) =
build(code, false, msg, data)
/**
* Build database successful response result object
*
* @param code Response code object
* @param msg Response message
* @param data Response data
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseCode
*/
fun <T> databaseSuccess(
code: ResponseCode = ResponseCode.DATABASE_SELECT_SUCCESS, msg: String = "success", data: T? = null
) = build(code, true, msg, data)
/**
* Build database failure response result object
*
* @param code Response code object
* @param msg Response message
* @param data Response data
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see ResponseCode
*/
fun <T> databaseFail(
code: ResponseCode = ResponseCode.DATABASE_SELECT_FAILED, msg: String = "fail", data: T? = null
) = build(code, false, msg, data)
}
}

View File

@@ -0,0 +1,55 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
/**
* Function entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_func")
class Func : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
/**
* Parent ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("parent_id")
var parentId: Long? = null
/**
* Menu ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("menu_id")
var menuId: Long? = null
override fun toString(): String {
return "Func(id=$id, name=$name, parentId=$parentId, menuId=$menuId)"
}
}

View File

@@ -0,0 +1,95 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
import java.time.LocalDateTime
/**
* Group entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_group")
class Group : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
/**
* Enable
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("enable")
var enable: Int? = null
/**
* Create time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("create_time", fill = FieldFill.INSERT)
var createTime: LocalDateTime? = null
/**
* Update time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("update_time", fill = FieldFill.INSERT_UPDATE)
var updateTime: LocalDateTime? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
/**
* Role list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Role
*/
@TableField(exist = false)
var roles: List<Role>? = null
override fun toString(): String {
return "Group(id=$id, name=$name, enable=$enable, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, roles=$roles)"
}
}

View File

@@ -0,0 +1,67 @@
package top.fatweb.oxygen.api.entity.permission
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonTypeInfo
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* Login user entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserDetails
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
class LoginUser() : UserDetails {
/**
* User object
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
*/
lateinit var user: User
@JsonIgnore
private var authorities: List<GrantedAuthority>? = null
constructor(user: User) : this() {
this.user = user
}
@JsonIgnore
override fun getAuthorities(): List<GrantedAuthority> {
authorities?.let { return it }
authorities = user.operations?.map { SimpleGrantedAuthority(it.code) } ?: emptyList()
return authorities as List<GrantedAuthority>
}
@JsonIgnore
override fun getPassword() = user.password
@JsonIgnore
override fun getUsername() = user.username
@JsonIgnore
override fun isAccountNonExpired() =
user.expiration == null || user.expiration!!.isAfter(LocalDateTime.now(ZoneOffset.UTC))
@JsonIgnore
override fun isAccountNonLocked() = user.locking == 0
@JsonIgnore
override fun isCredentialsNonExpired() =
user.credentialsExpiration == null || user.credentialsExpiration!!.isAfter(LocalDateTime.now(ZoneOffset.UTC))
@JsonIgnore
override fun isEnabled() = user.enable == 1
override fun toString(): String {
return "LoginUser(user=$user, authorities=$authorities)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
/**
* Menu entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_menu")
class Menu : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
/**
* URL
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("url")
var url: String? = null
/**
* Parent ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("parent_id")
var parentId: Long? = null
/**
* Module ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("module_id")
var moduleId: Long? = null
override fun toString(): String {
return "Menu(id=$id, name=$name, url=$url, parentId=$parentId, moduleId=$moduleId)"
}
}

View File

@@ -0,0 +1,37 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
/**
* Module Entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_module")
class Module : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
override fun toString(): String {
return "Module(id=$id, name=$name)"
}
}

View File

@@ -0,0 +1,55 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
/**
* Operation entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_operation")
class Operation : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
/**
* Code
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("code")
var code: String? = null
/**
* Function ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("func_id")
var funcId: Long? = null
override fun toString(): String {
return "Operation(id=$id, name=$name, code=$code, funcId=$funcId)"
}
}

View File

@@ -0,0 +1,37 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
/**
* Power entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_power")
class Power : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Type ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("type_id")
var typeId: Int? = null
override fun toString(): String {
return "Power(id=$id, typeId=$typeId)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
/**
* Power role intermediate entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_power_role")
class PowerRole : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Power ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("power_id")
var powerId: Long? = null
/**
* Role ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("role_id")
var roleId: Long? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
override fun toString(): String {
return "PowerRole(id=$id, powerId=$powerId, roleId=$roleId, deleted=$deleted, version=$version)"
}
}

View File

@@ -0,0 +1,51 @@
package top.fatweb.oxygen.api.entity.permission
import java.io.Serializable
/**
* Set of power entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
class PowerSet : Serializable {
/**
* Module list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Module
*/
var moduleList: List<Module>? = null
/**
* Menu list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Menu
*/
var menuList: List<Menu>? = null
/**
* Function list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Func
*/
var funcList: List<Func>? = null
/**
* Operation list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Operation
*/
var operationList: List<Operation>? = null
override fun toString(): String {
return "PowerSet(moduleList=$moduleList, menuList=$menuList, funcList=$funcList, operationList=$operationList)"
}
}

View File

@@ -0,0 +1,37 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
/**
* Power type entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_power_type")
class PowerType : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
override fun toString(): String {
return "PowerType(id=$id, name=$name)"
}
}

View File

@@ -0,0 +1,135 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
import java.time.LocalDateTime
/**
* Role entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_role")
class Role : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Name
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("name")
var name: String? = null
/**
* Enable
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("enable")
var enable: Int? = null
/**
* Create time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("create_time", fill = FieldFill.INSERT)
var createTime: LocalDateTime? = null
/**
* Update time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("update_time", fill = FieldFill.INSERT_UPDATE)
var updateTime: LocalDateTime? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
/**
* Module list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Module
*/
@TableField(exist = false)
var modules: List<Module>? = null
/**
* Menu list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Menu
*/
@TableField(exist = false)
var menus: List<Menu>? = null
/**
* Function list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Func
*/
@TableField(exist = false)
var funcs: List<Func>? = null
/**
* Operation list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Operation
*/
@TableField(exist = false)
var operations: List<Operation>? = null
/**
* Power list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Power
*/
@TableField(exist = false)
var powers: List<Power>? = null
override fun toString(): String {
return "Role(id=$id, name=$name, enable=$enable, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, modules=$modules, menus=$menus, funcs=$funcs, operations=$operations, powers=$powers)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
/**
* Role group intermediate entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_role_group")
class RoleGroup : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Role ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("role_id")
var roleId: Long? = null
/**
* Group ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("group_id")
var groupId: Long? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
override fun toString(): String {
return "RoleGroup(id=$id, roleId=$roleId, groupId=$groupId, deleted=$deleted, version=$version)"
}
}

View File

@@ -0,0 +1,256 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
import java.time.LocalDateTime
/**
* User entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_user")
class User() : Serializable {
constructor(id: Long?, username: String, password: String, enable: Boolean = true) : this() {
this.id = id
this.username = username
this.password = password
this.enable = if (enable) 1 else 0
}
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Username
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("username")
var username: String? = null
/**
* Password
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("password")
var password: String? = null
/**
* Verify email
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("verify")
var verify: String? = null
/**
* Forget password
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("forget")
var forget: String? = null
/**
* Locking
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("locking")
var locking: Int? = null
/**
* Expiration time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("expiration")
var expiration: LocalDateTime? = null
/**
* Credentials expiration time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("credentials_expiration")
var credentialsExpiration: LocalDateTime? = null
/**
* Enable
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("enable")
var enable: Int? = null
/**
* Current login time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("current_login_time")
var currentLoginTime: LocalDateTime? = null
/**
* Current login IP
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("current_login_ip")
var currentLoginIp: String? = null
/**
* Last login time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("last_login_time")
var lastLoginTime: LocalDateTime? = null
/**
* Last login IP
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("last_login_ip")
var lastLoginIp: String? = null
/**
* Create time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("create_time")
var createTime: LocalDateTime? = null
/**
* Update time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("update_time")
var updateTime: LocalDateTime? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
/**
* User info
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see UserInfo
*/
@TableField(exist = false)
var userInfo: UserInfo? = null
/**
* Role list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Role
*/
@TableField(exist = false)
var roles: List<Role>? = null
/**
* Group list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Group
*/
@TableField(exist = false)
var groups: List<Group>? = null
/**
* Module list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Module
*/
@TableField(exist = false)
var modules: List<Module>? = null
/**
* Menu list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Menu
*/
@TableField(exist = false)
var menus: List<Menu>? = null
/**
* Function list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Func
*/
@TableField(exist = false)
var funcs: List<Func>? = null
/**
* Operation list
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Operation
*/
@TableField(exist = false)
var operations: List<Operation>? = null
override fun toString(): String {
return "User(id=$id, username=$username, password=$password, verify=$verify, forget=$forget, locking=$locking, expiration=$expiration, credentialsExpiration=$credentialsExpiration, enable=$enable, currentLoginTime=$currentLoginTime, currentLoginIp=$currentLoginIp, lastLoginTime=$lastLoginTime, lastLoginIp=$lastLoginIp, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, userInfo=$userInfo, roles=$roles, groups=$groups, modules=$modules, menus=$menus, funcs=$funcs, operations=$operations)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
/**
* User group intermediate entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_user_group")
class UserGroup : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* User ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("user_id")
var userId: Long? = null
/**
* Group ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("group_id")
var groupId: Long? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
override fun toString(): String {
return "UserGroup(id=$id, userId=$userId, groupId=$groupId, deleted=$deleted, version=$version)"
}
}

View File

@@ -0,0 +1,103 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
import java.time.LocalDateTime
/**
* User information entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_user_info")
class UserInfo : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* User ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("user_id")
var userId: Long? = null
/**
* Nickname
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("nickname")
var nickname: String? = null
/**
* Avatar in base64
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("avatar")
var avatar: String? = null
/**
* Email
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("email")
var email: String? = null
/**
* Create time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("create_time", fill = FieldFill.INSERT)
var createTime: LocalDateTime? = null
/**
* Update time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@TableField("update_time", fill = FieldFill.INSERT_UPDATE)
var updateTime: LocalDateTime? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
override fun toString(): String {
return "UserInfo(id=$id, userId=$userId, nickname=$nickname, avatar=$avatar, email=$email, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
/**
* User role intermediate entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_user_role")
class UserRole : Serializable {
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* User ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("user_id")
var userId: Long? = null
/**
* Role ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("role_id")
var roleId: Long? = null
/**
* Deleted
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("deleted")
@TableLogic
var deleted: Long? = null
/**
* Version
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("version")
@Version
var version: Int? = null
override fun toString(): String {
return "UserRole(id=$id, userId=$userId, roleId=$roleId, deleted=$deleted, version=$version)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.system
import com.baomidou.mybatisplus.annotation.EnumValue
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonValue
import java.io.Serializable
import java.time.LocalDateTime
/**
* Event log entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_event_log")
class EventLog : Serializable {
enum class Event(@field:EnumValue @field:JsonValue val code: String) {
LOGIN("LOGIN"), LOGOUT("LOGOUT"), REGISTER("REGISTER"), VERIFY("VERIFY"), API("API")
}
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Event
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("event")
var event: Event? = null
/**
* Operate user ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("operate_user_id")
var operateUserId: Long? = null
/**
* Operate time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@TableField("operate_time")
var operateTime: LocalDateTime? = null
override fun toString(): String {
return "EventLog(id=$id, event=$event, operateUserId=$operateUserId, operateTime=$operateTime)"
}
}

View File

@@ -0,0 +1,64 @@
package top.fatweb.oxygen.api.entity.system
import com.baomidou.mybatisplus.annotation.EnumValue
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonValue
import java.io.Serializable
import java.time.LocalDateTime
/**
* Statistics log entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_statistics_log")
class StatisticsLog : Serializable {
enum class KeyItem(@field:EnumValue @field:JsonValue val code: String) {
ONLINE_USERS_COUNT("ONLINE_USER_COUNT")
}
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Key
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("key")
var key: KeyItem? = null
/**
* Value
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("value")
var value: String? = null
/**
* Record time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@TableField("record_time")
var recordTime: LocalDateTime? = null
override fun toString(): String {
return "StatisticsLog(id=$id, key=$key, value=$value, recordTime=$recordTime)"
}
}

View File

@@ -0,0 +1,186 @@
package top.fatweb.oxygen.api.entity.system
import com.baomidou.mybatisplus.annotation.EnumValue
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonValue
import java.io.Serializable
import java.time.LocalDateTime
/**
* System log entity
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableName("t_sys_log")
class SysLog : Serializable {
/**
* Log type enum
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
enum class LogType(@field:EnumValue @field:JsonValue val code: String) {
INFO("INFO"), ERROR("ERROR"), LOGIN("LOGIN"), LOGOUT("LOGOUT"), REGISTER("REGISTER"), STATISTICS("STATISTICS"), API(
"API"
)
}
/**
* ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableId("id")
var id: Long? = null
/**
* Log type
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LogType
*/
@TableField("log_type")
var logType: LogType? = null
/**
* Operate user ID
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("operate_user_id")
var operateUserId: Long? = null
/**
* Operate time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@TableField("operate_time")
var operateTime: LocalDateTime? = null
/**
* Request URI
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("request_uri")
var requestUri: String? = null
/**
* Request method
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("request_method")
var requestMethod: String? = null
/**
* Request parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("request_params")
var requestParams: String? = null
/**
* Request IP
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("request_ip")
var requestIp: String? = null
/**
* Request server address
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("request_server_address")
var requestServerAddress: String? = null
/**
* Is exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("exception")
var exception: Int? = null
/**
* Exception information
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("exception_info")
var exceptionInfo: String? = null
/**
* Start time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@TableField("start_time")
var startTime: LocalDateTime? = null
/**
* End time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see LocalDateTime
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@TableField("end_time")
var endTime: LocalDateTime? = null
/**
* Execute time
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("execute_time")
var executeTime: Long? = null
/**
* User agent
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("user_agent")
var userAgent: String? = null
/**
* Operate username
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField(exist = false)
var operateUsername: String? = null
override fun toString(): String {
return "SysLog(id=$id, logType=$logType, operateUserId=$operateUserId, operateTime=$operateTime, requestUri=$requestUri, requestMethod=$requestMethod, requestParams=$requestParams, requestIp=$requestIp, requestServerAddress=$requestServerAddress, exception=$exception, exceptionInfo=$exceptionInfo, startTime=$startTime, endTime=$endTime, executeTime=$executeTime, userAgent=$userAgent, operateUsername=$operateUsername)"
}
}

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* Account need initialize exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class AccountNeedInitException : RuntimeException("Account need initialize")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* Account need reset password exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class AccountNeedResetPasswordException : RuntimeException("Account need reset password")

View File

@@ -0,0 +1,13 @@
package top.fatweb.oxygen.api.exception
/**
* Email settings not configured exception
*
* @param configs Configs not config
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class NoEmailConfigException(
vararg configs: String
) : RuntimeException("Email settings not configured: ${configs.joinToString(", ")}")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* No record found exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class NoRecordFoundException : RuntimeException("No record found")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* No verification required exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class NoVerificationRequiredException : RuntimeException("No verification required")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* Retrieve code error or expired exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class RetrieveCodeErrorOrExpiredException : RuntimeException("Retrieve code error or expired")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* Token has expired exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class TokenHasExpiredException : RuntimeException("Token has expired")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* User not found exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class UserNotFoundException : RuntimeException("User not found")

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.api.exception
/**
* Verification code error or expired exception
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RuntimeException
*/
class VerificationCodeErrorOrExpiredException : RuntimeException("Verification code is error or has expired")

View File

@@ -0,0 +1,28 @@
package top.fatweb.oxygen.api.filter
import jakarta.servlet.Filter
import jakarta.servlet.FilterChain
import jakarta.servlet.ServletRequest
import jakarta.servlet.ServletResponse
import org.springframework.stereotype.Component
/**
* Exception filter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Filter
*/
@Component
class ExceptionFilter : Filter {
override fun doFilter(
servletRequest: ServletRequest?, servletResponse: ServletResponse?, filterChain: FilterChain?
) {
try {
filterChain!!.doFilter(servletRequest, servletResponse)
} catch (e: Exception) {
servletRequest?.setAttribute("filter.error", e)
servletRequest?.getRequestDispatcher("/error/thrown")?.forward(servletRequest, servletResponse)
}
}
}

View File

@@ -0,0 +1,57 @@
package top.fatweb.oxygen.api.filter
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import org.springframework.util.StringUtils
import org.springframework.web.filter.OncePerRequestFilter
import top.fatweb.oxygen.api.entity.permission.LoginUser
import top.fatweb.oxygen.api.exception.TokenHasExpiredException
import top.fatweb.oxygen.api.properties.SecurityProperties
import top.fatweb.oxygen.api.util.JwtUtil
import top.fatweb.oxygen.api.util.RedisUtil
import top.fatweb.oxygen.api.util.WebUtil
/**
* Jwt authentication token filter
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see RedisUtil
* @see OncePerRequestFilter
*/
@Component
class JwtAuthenticationTokenFilter(private val redisUtil: RedisUtil) : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain
) {
val tokenWithPrefix = request.getHeader(SecurityProperties.headerKey)
if (!StringUtils.hasText(tokenWithPrefix) || "/error/thrown" == request.servletPath) {
filterChain.doFilter(request, response)
return
}
val token = WebUtil.getToken(tokenWithPrefix)
JwtUtil.parseJwt(token)
val redisKeyPattern = "${SecurityProperties.jwtIssuer}_login_*:" + token
val redisKeys = redisUtil.keys(redisKeyPattern)
if (redisKeys.isEmpty()) {
throw TokenHasExpiredException()
}
val loginUser = redisUtil.getObject<LoginUser>(redisKeys.first())
loginUser ?: let { throw TokenHasExpiredException() }
redisUtil.setExpire(redisKeys.first(), SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit)
val authenticationToken = UsernamePasswordAuthenticationToken(loginUser, null, loginUser.authorities)
SecurityContextHolder.getContext().authentication = authenticationToken
filterChain.doFilter(request, response)
}
}

View File

@@ -0,0 +1,26 @@
package top.fatweb.oxygen.api.handler
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
import org.apache.ibatis.reflection.MetaObject
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* Date meta object handler
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see MetaObjectHandler
*/
@Component
class DataMetaObjectHandler : MetaObjectHandler {
override fun insertFill(metaObject: MetaObject?) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime::class.java, LocalDateTime.now(ZoneOffset.UTC))
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::class.java, LocalDateTime.now(ZoneOffset.UTC))
}
override fun updateFill(metaObject: MetaObject?) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::class.java, LocalDateTime.now(ZoneOffset.UTC))
}
}

View File

@@ -0,0 +1,181 @@
package top.fatweb.oxygen.api.handler
import com.auth0.jwt.exceptions.JWTDecodeException
import com.auth0.jwt.exceptions.SignatureVerificationException
import com.auth0.jwt.exceptions.TokenExpiredException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.dao.DuplicateKeyException
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.jdbc.BadSqlGrammarException
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.authentication.*
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import top.fatweb.avatargenerator.AvatarException
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.exception.*
/**
* Exception handler
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@RestControllerAdvice
class ExceptionHandler {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
/**
* Handle all exception
*
* @param e Exception
* @return Response object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Exception
* @see ResponseResult
*/
@ExceptionHandler(value = [Exception::class])
fun exceptionHandler(e: Exception): ResponseResult<*> {
return when (e) {
is HttpRequestMethodNotSupportedException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.SYSTEM_REQUEST_ILLEGAL, e.localizedMessage, null)
}
is HttpMessageNotReadableException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.SYSTEM_REQUEST_ILLEGAL, e.localizedMessage.split(":")[0], null)
}
is MethodArgumentNotValidException -> {
logger.debug(e.localizedMessage, e)
val errorMessage = e.allErrors.map { error -> error.defaultMessage }.joinToString(". ")
ResponseResult.fail(ResponseCode.SYSTEM_ARGUMENT_NOT_VALID, errorMessage, null)
}
is InsufficientAuthenticationException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_UNAUTHORIZED, e.localizedMessage, null)
}
is LockedException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_USER_LOCKED, "User account has been locked", null)
}
is AccountExpiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_USER_EXPIRED, "User account has expired", null)
}
is CredentialsExpiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(
ResponseCode.PERMISSION_USER_CREDENTIALS_EXPIRED,
"User credentials have expired",
null
)
}
is DisabledException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_USER_CREDENTIALS_EXPIRED, "User has been disabled", null)
}
is TokenExpiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_TOKEN_HAS_EXPIRED, e.localizedMessage, null)
}
is InternalAuthenticationServiceException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_USERNAME_NOT_FOUND, "Username not found", null)
}
is BadCredentialsException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(
ResponseCode.PERMISSION_LOGIN_USERNAME_PASSWORD_ERROR,
"Wrong user name or password",
null
)
}
is SignatureVerificationException, is JWTDecodeException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_TOKEN_ILLEGAL, "Token illegal", null)
}
is TokenHasExpiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_TOKEN_HAS_EXPIRED, e.localizedMessage, null)
}
is AccessDeniedException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_ACCESS_DENIED, "Access Denied", null)
}
is UserNotFoundException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_USER_NOT_FOUND, e.localizedMessage, null)
}
is NoVerificationRequiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_NO_VERIFICATION_REQUIRED, e.localizedMessage, null)
}
is VerificationCodeErrorOrExpiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_VERIFY_CODE_ERROR_OR_EXPIRED, e.localizedMessage, null)
}
is AccountNeedInitException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_ACCOUNT_NEED_INIT, e.localizedMessage, null)
}
is RetrieveCodeErrorOrExpiredException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_RETRIEVE_CODE_ERROR_OR_EXPIRED, e.localizedMessage, null)
}
is AccountNeedResetPasswordException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.PERMISSION_ACCOUNT_NEED_RESET_PASSWORD, e.localizedMessage, null)
}
is BadSqlGrammarException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.DATABASE_EXECUTE_ERROR, "Incorrect SQL syntax", null)
}
is DuplicateKeyException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.DATABASE_DUPLICATE_KEY, "Duplicate key", null)
}
is NoRecordFoundException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.DATABASE_NO_RECORD_FOUND, e.localizedMessage, null)
}
is AvatarException -> {
logger.debug(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.API_AVATAR_ERROR, e.localizedMessage, null)
}
else -> {
logger.error(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.SYSTEM_ERROR, e.toString(), null)
}
}
}
}

View File

@@ -0,0 +1,24 @@
package top.fatweb.oxygen.api.handler
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.stereotype.Component
/**
* Jwt access denied handler
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AccessDeniedHandler
*/
@Component
class JwtAccessDeniedHandler : AccessDeniedHandler {
override fun handle(
request: HttpServletRequest?, response: HttpServletResponse?, accessDeniedException: AccessDeniedException?
) {
request?.setAttribute("filter.error", accessDeniedException)
request?.getRequestDispatcher("/error/thrown")?.forward(request, response)
}
}

View File

@@ -0,0 +1,24 @@
package top.fatweb.oxygen.api.handler
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.stereotype.Component
/**
* Jwt authentication entry point handler
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see AuthenticationEntryPoint
*/
@Component
class JwtAuthenticationEntryPointHandler : AuthenticationEntryPoint {
override fun commence(
request: HttpServletRequest?, response: HttpServletResponse?, authException: AuthenticationException?
) {
request?.setAttribute("filter.error", authException)
request?.getRequestDispatcher("/error/thrown")?.forward(request, response)
}
}

View File

@@ -0,0 +1,134 @@
package top.fatweb.oxygen.api.interceptor
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.core.MethodParameter
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.servlet.HandlerInterceptor
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
import top.fatweb.oxygen.api.entity.common.ResponseCode
import top.fatweb.oxygen.api.entity.common.ResponseResult
import top.fatweb.oxygen.api.entity.system.SysLog
import top.fatweb.oxygen.api.service.system.ISysLogService
import top.fatweb.oxygen.api.util.WebUtil
import top.fatweb.oxygen.api.vo.permission.LoginVo
import java.net.URI
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.temporal.ChronoUnit
import java.util.*
import java.util.concurrent.Executor
/**
* System log interceptor
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Executor
* @see ISysLogService
*/
@ControllerAdvice
class SysLogInterceptor(
@Qualifier("applicationTaskExecutor") private val customThreadPoolTaskExecutor: Executor,
private val sysLogService: ISysLogService
) : HandlerInterceptor, ResponseBodyAdvice<Any> {
private val sysLogThreadLocal = ThreadLocal<SysLog>()
private val resultThreadLocal = ThreadLocal<Any>()
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val sysLog = SysLog().apply {
operateUserId = WebUtil.getLoginUserId() ?: -1
startTime = LocalDateTime.now(ZoneOffset.UTC)
requestUri = URI(request.requestURI).path
requestParams = formatParams(request.parameterMap)
requestMethod = request.method
requestIp = request.remoteAddr
requestServerAddress = "${request.scheme}://${request.serverName}:${request.serverPort}"
userAgent = request.getHeader("User-Agent")
}
sysLogThreadLocal.set(sysLog)
return true
}
override fun afterCompletion(
request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?
) {
val sysLog = sysLogThreadLocal.get()
val result = resultThreadLocal.get()
sysLog.endTime = LocalDateTime.now(ZoneOffset.UTC)
sysLog.executeTime = ChronoUnit.MILLIS.between(sysLog.startTime, sysLog.endTime)
if (result is ResponseResult<*>) {
if (result.success) {
sysLog.apply {
logType = requestUri?.let {
when {
it.startsWith("/login") -> SysLog.LogType.LOGIN
it.startsWith("/logout") -> SysLog.LogType.LOGOUT
it.startsWith("/register") -> SysLog.LogType.REGISTER
it.startsWith("/system/statistics/") -> SysLog.LogType.STATISTICS
it.startsWith("/api/") -> SysLog.LogType.API
else -> SysLog.LogType.INFO
}
} ?: SysLog.LogType.INFO
exception = 0
}
if (result.data is LoginVo) {
sysLog.operateUserId = result.data.userId ?: -1
}
} else {
sysLog.apply {
logType = SysLog.LogType.ERROR
exception = 1
exceptionInfo = result.msg
}
}
customThreadPoolTaskExecutor.execute(SaveLogThread(sysLog, sysLogService))
}
sysLogThreadLocal.remove()
}
private fun formatParams(parameterMap: Map<String, Array<String>>): String {
val params = StringJoiner("&")
parameterMap.forEach {
params.add("${it.key}=${if (it.key.endsWith("password", true)) "*" else it.value.joinToString(",")}")
}
return params.toString()
}
private class SaveLogThread(val sysLog: SysLog, val sysLogService: ISysLogService) : Thread() {
override fun run() {
sysLog.operateTime = LocalDateTime.now(ZoneOffset.UTC)
sysLogService.save(sysLog)
}
}
override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean =
true
override fun beforeBodyWrite(
body: Any?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class<out HttpMessageConverter<*>>,
request: ServerHttpRequest,
response: ServerHttpResponse
): Any? {
resultThreadLocal.set(body)
if (body is ResponseResult<*> && body.code == ResponseCode.SYSTEM_ERROR.code) {
return ResponseResult.build(body.code, body.success, "fail", body.data)
}
return body
}
}

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.Func
/**
* Function mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Func
*/
@Mapper
interface FuncMapper : BaseMapper<Func>

View File

@@ -0,0 +1,57 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.baomidou.mybatisplus.core.metadata.IPage
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Param
import top.fatweb.oxygen.api.entity.permission.Group
/**
* Group mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Group
*/
@Mapper
interface GroupMapper : BaseMapper<Group> {
/**
* Select group in page
*
* @param page Pagination
* @param searchName Name to search for
* @param searchRegex Use regex
* @return Group in page
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
*/
fun selectPage(
page: IPage<Long>,
@Param("searchName") searchName: String?,
@Param("searchRegex") searchRegex: Boolean
): IPage<Long>
/**
* Select group with role list by list of group IDs
*
* @param groupIds List of group IDs
* @return Group with role list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Group
*/
fun selectListWithRoleByIds(@Param("groupIds") groupIds: List<Long>): List<Group>?
/**
* Select one group by ID
*
* @param id Group ID
* @return Group object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Group
*/
fun selectOneById(@Param("id") id: Long): Group?
}

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.Menu
/**
* Menu mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Menu
*/
@Mapper
interface MenuMapper : BaseMapper<Menu>

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.Module
/**
* Module mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Module
*/
@Mapper
interface ModuleMapper : BaseMapper<Module>

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.Operation
/**
* Operation mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Operation
*/
@Mapper
interface OperationMapper : BaseMapper<Operation>

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.Power
/**
* Power mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Power
*/
@Mapper
interface PowerMapper : BaseMapper<Power>

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.PowerRole
/**
* Power role intermediate mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see PowerRole
*/
@Mapper
interface PowerRoleMapper : BaseMapper<PowerRole>

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.RoleGroup
/**
* Role group intermediate mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see RoleGroup
*/
@Mapper
interface RoleGroupMapper : BaseMapper<RoleGroup>

View File

@@ -0,0 +1,57 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.baomidou.mybatisplus.core.metadata.IPage
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Param
import top.fatweb.oxygen.api.entity.permission.Role
/**
* Role mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see Role
*/
@Mapper
interface RoleMapper : BaseMapper<Role> {
/**
* Select role in page
*
* @param page Pagination
* @param searchName Name to search for
* @param searchRegex Use regex
* @return Role in page
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
*/
fun selectPage(
page: IPage<Long>,
@Param("searchName") searchName: String?,
@Param("searchRegex") searchRegex: Boolean
): IPage<Long>
/**
* Select role with power list by list of role IDs
*
* @param roleIds List of role IDs
* @return Role with power list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Role
*/
fun selectListWithPowerByIds(@Param("roleIds") roleIds: List<Long>): List<Role>?
/**
* Select one role by ID
*
* @param id Role ID
* @return Role object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see Role
*/
fun selectOneById(@Param("id") id: Long): Role?
}

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.UserGroup
/**
* User group intermediate mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see UserGroup
*/
@Mapper
interface UserGroupMapper : BaseMapper<UserGroup>

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.UserInfo
/**
* User information mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see UserInfo
*/
@Mapper
interface UserInfoMapper : BaseMapper<UserInfo>

View File

@@ -0,0 +1,100 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.baomidou.mybatisplus.core.metadata.IPage
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Param
import top.fatweb.oxygen.api.entity.permission.User
/**
* User mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see User
*/
@Mapper
interface UserMapper : BaseMapper<User> {
/**
* Select one user with power and information by username or email
*
* @param account Username or email
* @return User object with power and information
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
*/
fun selectOneWithPowerInfoByAccount(@Param("account") account: String): User?
/**
* Select user in page
*
* @param page Pagination
* @param searchType Type of search
* @param searchValue Value to search for
* @param searchRegex Use regex
* @return User in page
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
*/
fun selectPage(
page: IPage<Long>,
@Param("searchType") searchType: String,
@Param("searchValue") searchValue: String?,
@Param("searchRegex") searchRegex: Boolean
): IPage<Long>
/**
* Select user with role and information list by list of user IDs
*
* @param userIds List of user IDs
* @return User with role and information list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
*/
fun selectListWithRoleInfoByIds(@Param("userIds") userIds: List<Long>): List<User>
/**
* Select one user by ID
*
* @param id User ID
* @return User object
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
*/
fun selectOneWithRoleInfoById(@Param("id") id: Long): User?
/**
* Select all user with information list
*
* @return User with information list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see User
*/
fun selectListWithInfo(): List<User>
/**
* Select user IDs list by list of role IDs
*
* @param roleIds List of role IDs
* @return User IDs list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun selectIdsWithRoleIds(@Param("roleIds") roleIds: List<Long>): List<Long>
/**
* Select user IDs list by list of group IDs
*
* @param groupIds List of group IDs
* @return User IDs list
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun selectIdsWithGroupIds(@Param("groupIds") groupIds: List<Long>): List<Long>
}

View File

@@ -0,0 +1,16 @@
package top.fatweb.oxygen.api.mapper.permission
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.permission.UserRole
/**
* User role intermediate mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see UserRole
*/
@Mapper
interface UserRoleMapper : BaseMapper<UserRole>

View File

@@ -0,0 +1,14 @@
package top.fatweb.oxygen.api.mapper.system
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.system.EventLog
/**
* Event log mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Mapper
interface EventLogMapper : BaseMapper<EventLog>

View File

@@ -0,0 +1,14 @@
package top.fatweb.oxygen.api.mapper.system
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import org.apache.ibatis.annotations.Mapper
import top.fatweb.oxygen.api.entity.system.StatisticsLog
/**
* Statistics log mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Mapper
interface StatisticsLogMapper : BaseMapper<StatisticsLog>

View File

@@ -0,0 +1,44 @@
package top.fatweb.oxygen.api.mapper.system
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.baomidou.mybatisplus.core.metadata.IPage
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Param
import top.fatweb.oxygen.api.entity.system.SysLog
import java.time.LocalDateTime
/**
* System log mapper
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see BaseMapper
* @see SysLog
*/
@Mapper
interface SysLogMapper : BaseMapper<SysLog> {
/**
* Select system log in page
*
* @param page Pagination
* @param logType List of log types
* @param requestMethod List of request methods
* @param searchRequestUrl Request URL to search for
* @param searchStartTime Start time to search for
* @param searchEndTime end time to search for
* @return System log in page
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
* @see IPage
* @see SysLog
* @see LocalDateTime
*/
fun selectPage(
page: IPage<SysLog>,
@Param("logType") logType: List<String>?,
@Param("requestMethod") requestMethod: List<String>?,
@Param("searchRequestUrl") searchRequestUrl: String?,
@Param("searchStartTime") searchStartTime: LocalDateTime?,
@Param("searchEndTime") searchEndTime: LocalDateTime?
): IPage<SysLog>
}

View File

@@ -0,0 +1,50 @@
package top.fatweb.oxygen.api.param
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Min
/**
* Page sort parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
open class PageSortParam {
/**
* Current page number
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "分页页码", defaultValue = "1", example = "1")
@field:Min(1, message = "Pagination page number must be a positive integer")
var currentPage: Long = 1
/**
* Size of page
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "分页大小", defaultValue = "20", example = "20")
@field:Min(1, message = "The number of data per page must be a positive integer")
var pageSize: Long = 20
/**
* Field name to sort
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "排序字段", example = "id")
var sortField: String? = null
/**
* Sort order by
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "排序方式", allowableValues = ["desc", "asc"], defaultValue = "desc", example = "desc")
var sortOrder: String? = null
}

View File

@@ -0,0 +1,69 @@
package top.fatweb.oxygen.api.param.api.v1.avatar
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Pattern
/**
* Avatar base parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
open class AvatarBaseParam {
/**
* Seed to generate avatar
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "种子")
var seed: Long? = null
/**
* Size of image
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "图像大小", defaultValue = "128")
@field:Max(256, message = "Size must be less than or equal to 256")
var size: Int? = null
/**
* Margin of image
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "外边距", defaultValue = "0")
var margin: Int? = null
/**
* Padding of image
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "内边距", defaultValue = "0")
var padding: Int? = null
/**
* List of colors to generate avatar
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(defaultValue = "颜色列表", example = "#FFFFFFAA")
var colors: List<String>? = null
/**
* Background of image
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(defaultValue = "背景颜色", example = "#FFFFFFAA")
@field:Pattern(regexp = "^#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8}$", message = "Background color must be a hex color code")
var background: String? = null
}

View File

@@ -0,0 +1,31 @@
package top.fatweb.oxygen.api.param.api.v1.avatar
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Max
/**
* GitHub style avatar parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
data class AvatarGitHubParam(
/**
* Size of element
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "元素大小", defaultValue = "400")
@field:Max(1000, message = "Element size must be less than or equal to 1000")
val elementSize: Int = 400,
/**
* Precision of element
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "精确度", defaultValue = "5")
val precision: Int = 5
) : AvatarBaseParam()

View File

@@ -0,0 +1,25 @@
package top.fatweb.oxygen.api.param.permission
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern
/**
* Forget password parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "忘记密码请求参数")
data class ForgetParam(
/**
* Email
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "邮箱", required = true, example = "user@email.com")
@field:NotBlank(message = "Email can not be blank")
@field:Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address")
val email: String?
)

View File

@@ -0,0 +1,33 @@
package top.fatweb.oxygen.api.param.permission
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
/**
* Login parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "登录请求参数")
data class LoginParam(
/**
* Account
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "账户", required = true, example = "test")
@field:NotBlank(message = "Account can not be blank")
val account: String?,
/**
* Password
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "密码", required = true)
@field:NotBlank(message = "Password can not be blank")
val password: String?
)

View File

@@ -0,0 +1,48 @@
package top.fatweb.oxygen.api.param.permission
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.Size
/**
* Register parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "注册请求参数")
data class RegisterParam(
/**
* Username
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "用户名", required = true, example = "abc")
@field:NotBlank(message = "Username can not be blank")
@field:Pattern(regexp = "[a-zA-Z-_][0-9a-zA-Z-_]{2,38}", message = "Illegal username")
val username: String?,
/**
* Email
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "邮箱", required = true, example = "user@email.com")
@field:NotBlank(message = "Email can not be blank")
@field:Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address")
val email: String?,
/**
* Password
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "密码", required = true)
@field:NotBlank(message = "Password can not be blank")
@field:Size(min = 10, max = 30)
val password: String?
)

View File

@@ -0,0 +1,35 @@
package top.fatweb.oxygen.api.param.permission
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size
/**
* Retrieve password parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "找回密码请求参数")
data class RetrieveParam(
/**
* Code
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "验证码", required = true)
@field:NotBlank(message = "Code can not be blank")
val code: String?,
/**
* New password
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "新密码")
@field:NotBlank(message = "New password can not be blank")
@field:Size(min = 10, max = 30)
val password: String?
)

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