Add active information api
This commit is contained in:
@@ -12,6 +12,7 @@ import top.fatweb.api.annotation.EventLogRecord
|
|||||||
import top.fatweb.api.entity.system.EventLog
|
import top.fatweb.api.entity.system.EventLog
|
||||||
import top.fatweb.api.service.system.IEventLogService
|
import top.fatweb.api.service.system.IEventLogService
|
||||||
import top.fatweb.api.util.WebUtil
|
import top.fatweb.api.util.WebUtil
|
||||||
|
import top.fatweb.api.vo.permission.LoginVo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event log record aspect
|
* Event log record aspect
|
||||||
@@ -27,16 +28,18 @@ class EventLogAspect(
|
|||||||
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@Pointcut("@annotation(top.fatweb.api.annotation.EventLogRecord)")
|
@Pointcut("@annotation(top.fatweb.api.annotation.EventLogRecord)")
|
||||||
fun eventLogPointcut() {}
|
fun eventLogPointcut() {
|
||||||
|
}
|
||||||
|
|
||||||
@AfterReturning("eventLogPointcut()")
|
@AfterReturning(value = "eventLogPointcut()", returning = "retValue")
|
||||||
fun doAfter(joinPoint: JoinPoint) {
|
fun doAfter(joinPoint: JoinPoint, retValue: Any) {
|
||||||
val annotation = (joinPoint.signature as MethodSignature).method.getAnnotation(EventLogRecord::class.java)
|
val annotation = (joinPoint.signature as MethodSignature).method.getAnnotation(EventLogRecord::class.java)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
eventLogService.save(EventLog().apply {
|
eventLogService.save(EventLog().apply {
|
||||||
this.event = annotation.event
|
this.event = annotation.event
|
||||||
operateUserId = WebUtil.getLoginUserId() ?: -1
|
operateUserId = WebUtil.getLoginUserId()
|
||||||
|
?: if (retValue is LoginVo) retValue.userId else -1
|
||||||
})
|
})
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Cannot record event!!!", e)
|
logger.error("Cannot record event!!!", e)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.Operation
|
|||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import top.fatweb.api.annotation.BaseController
|
import top.fatweb.api.annotation.BaseController
|
||||||
import top.fatweb.api.entity.common.ResponseResult
|
import top.fatweb.api.entity.common.ResponseResult
|
||||||
|
import top.fatweb.api.param.system.ActiveInfoGetParam
|
||||||
import top.fatweb.api.param.system.OnlineInfoGetParam
|
import top.fatweb.api.param.system.OnlineInfoGetParam
|
||||||
import top.fatweb.api.service.system.IStatisticService
|
import top.fatweb.api.service.system.IStatisticService
|
||||||
import top.fatweb.api.vo.system.*
|
import top.fatweb.api.vo.system.*
|
||||||
@@ -72,7 +73,7 @@ class StatisticController(
|
|||||||
fun storage(): ResponseResult<StorageInfoVo> = ResponseResult.success(data = statisticService.storage())
|
fun storage(): ResponseResult<StorageInfoVo> = ResponseResult.success(data = statisticService.storage())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of online users information
|
* Get the history of online users information
|
||||||
*
|
*
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
@@ -81,4 +82,15 @@ class StatisticController(
|
|||||||
@GetMapping("/online")
|
@GetMapping("/online")
|
||||||
fun online(onlineInfoGetParam: OnlineInfoGetParam?): ResponseResult<OnlineInfoVo> =
|
fun online(onlineInfoGetParam: OnlineInfoGetParam?): ResponseResult<OnlineInfoVo> =
|
||||||
ResponseResult.success(data = statisticService.online(onlineInfoGetParam))
|
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")
|
||||||
|
fun active(activeInfoGetParam: ActiveInfoGetParam): ResponseResult<ActiveInfoVo> =
|
||||||
|
ResponseResult.success(data = statisticService.active(activeInfoGetParam))
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@ import top.fatweb.api.entity.common.ResponseResult
|
|||||||
import top.fatweb.api.entity.system.SysLog
|
import top.fatweb.api.entity.system.SysLog
|
||||||
import top.fatweb.api.service.system.ISysLogService
|
import top.fatweb.api.service.system.ISysLogService
|
||||||
import top.fatweb.api.util.WebUtil
|
import top.fatweb.api.util.WebUtil
|
||||||
|
import top.fatweb.api.vo.permission.LoginVo
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
@@ -33,7 +34,8 @@ import java.util.concurrent.Executor
|
|||||||
*/
|
*/
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
class SysLogInterceptor(
|
class SysLogInterceptor(
|
||||||
@Qualifier("applicationTaskExecutor") private val customThreadPoolTaskExecutor: Executor, private val sysLogService: ISysLogService
|
@Qualifier("applicationTaskExecutor") private val customThreadPoolTaskExecutor: Executor,
|
||||||
|
private val sysLogService: ISysLogService
|
||||||
) : HandlerInterceptor, ResponseBodyAdvice<Any> {
|
) : HandlerInterceptor, ResponseBodyAdvice<Any> {
|
||||||
private val sysLogThreadLocal = ThreadLocal<SysLog>()
|
private val sysLogThreadLocal = ThreadLocal<SysLog>()
|
||||||
private val resultThreadLocal = ThreadLocal<Any>()
|
private val resultThreadLocal = ThreadLocal<Any>()
|
||||||
@@ -77,6 +79,9 @@ class SysLogInterceptor(
|
|||||||
} ?: SysLog.LogType.INFO
|
} ?: SysLog.LogType.INFO
|
||||||
exception = 0
|
exception = 0
|
||||||
}
|
}
|
||||||
|
if (result.data is LoginVo) {
|
||||||
|
sysLog.operateUserId = result.data.userId ?: -1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sysLog.apply {
|
sysLog.apply {
|
||||||
logType = SysLog.LogType.ERROR
|
logType = SysLog.LogType.ERROR
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package top.fatweb.api.param.system
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.EnumValue
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue
|
||||||
|
|
||||||
|
data class ActiveInfoGetParam(
|
||||||
|
val scope: Scope = Scope.WEAK
|
||||||
|
) {
|
||||||
|
enum class Scope(@field:EnumValue @field:JsonValue val code: String) {
|
||||||
|
WEAK("WEAK"),
|
||||||
|
|
||||||
|
MONTH("MONTH"),
|
||||||
|
|
||||||
|
QUARTER("QUARTER"),
|
||||||
|
|
||||||
|
YEAR("YEAR"),
|
||||||
|
|
||||||
|
TWO_YEARS("TWO_YEARS"),
|
||||||
|
|
||||||
|
THREE_YEARS("THREE_YEARS"),
|
||||||
|
|
||||||
|
FIVE_YEARS("FIVE_YEARS"),
|
||||||
|
|
||||||
|
ALL("ALL")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,7 +70,7 @@ class AuthenticationServiceImpl(
|
|||||||
val redisKey = "${SecurityProperties.jwtIssuer}_login_${userId}:" + jwt
|
val redisKey = "${SecurityProperties.jwtIssuer}_login_${userId}:" + jwt
|
||||||
redisUtil.setObject(redisKey, loginUser, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit)
|
redisUtil.setObject(redisKey, loginUser, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit)
|
||||||
|
|
||||||
return LoginVo(jwt, loginUser.user.currentLoginTime, loginUser.user.currentLoginIp)
|
return LoginVo(jwt, loginUser.user.id, loginUser.user.currentLoginTime, loginUser.user.currentLoginIp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventLogRecord(EventLog.Event.LOGOUT)
|
@EventLogRecord(EventLog.Event.LOGOUT)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package top.fatweb.api.service.system
|
package top.fatweb.api.service.system
|
||||||
|
|
||||||
|
import top.fatweb.api.param.system.ActiveInfoGetParam
|
||||||
import top.fatweb.api.param.system.OnlineInfoGetParam
|
import top.fatweb.api.param.system.OnlineInfoGetParam
|
||||||
import top.fatweb.api.vo.system.*
|
import top.fatweb.api.vo.system.*
|
||||||
|
|
||||||
@@ -51,10 +52,18 @@ interface IStatisticService {
|
|||||||
fun storage(): StorageInfoVo
|
fun storage(): StorageInfoVo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of online users information
|
* Get the history of online users information
|
||||||
*
|
*
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
fun online(onlineInfoGetParam: OnlineInfoGetParam?): OnlineInfoVo
|
fun online(onlineInfoGetParam: OnlineInfoGetParam?): OnlineInfoVo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the history of active information
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
fun active(activeInfoGetParam: ActiveInfoGetParam?): ActiveInfoVo
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
package top.fatweb.api.service.system.impl
|
package top.fatweb.api.service.system.impl
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
|
||||||
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
|
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import oshi.SystemInfo
|
import oshi.SystemInfo
|
||||||
import oshi.hardware.CentralProcessor
|
import oshi.hardware.CentralProcessor
|
||||||
|
import top.fatweb.api.entity.system.EventLog
|
||||||
import top.fatweb.api.entity.system.StatisticLog
|
import top.fatweb.api.entity.system.StatisticLog
|
||||||
|
import top.fatweb.api.param.system.ActiveInfoGetParam
|
||||||
import top.fatweb.api.param.system.OnlineInfoGetParam
|
import top.fatweb.api.param.system.OnlineInfoGetParam
|
||||||
import top.fatweb.api.properties.SecurityProperties
|
import top.fatweb.api.properties.SecurityProperties
|
||||||
import top.fatweb.api.properties.ServerProperties
|
import top.fatweb.api.properties.ServerProperties
|
||||||
|
import top.fatweb.api.service.system.IEventLogService
|
||||||
import top.fatweb.api.service.system.IStatisticLogService
|
import top.fatweb.api.service.system.IStatisticLogService
|
||||||
import top.fatweb.api.service.system.IStatisticService
|
import top.fatweb.api.service.system.IStatisticService
|
||||||
import top.fatweb.api.util.ByteUtil
|
import top.fatweb.api.util.ByteUtil
|
||||||
import top.fatweb.api.util.RedisUtil
|
import top.fatweb.api.util.RedisUtil
|
||||||
import top.fatweb.api.vo.system.*
|
import top.fatweb.api.vo.system.*
|
||||||
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@@ -27,7 +33,8 @@ import java.util.concurrent.TimeUnit
|
|||||||
@Service
|
@Service
|
||||||
class StatisticServiceImpl(
|
class StatisticServiceImpl(
|
||||||
private val redisUtil: RedisUtil,
|
private val redisUtil: RedisUtil,
|
||||||
private val statisticLogService: IStatisticLogService
|
private val statisticLogService: IStatisticLogService,
|
||||||
|
private val eventLogService: IEventLogService
|
||||||
) : IStatisticService {
|
) : IStatisticService {
|
||||||
private val systemProperties: Properties = System.getProperties()
|
private val systemProperties: Properties = System.getProperties()
|
||||||
private val systemInfo: SystemInfo = SystemInfo()
|
private val systemInfo: SystemInfo = SystemInfo()
|
||||||
@@ -171,8 +178,8 @@ class StatisticServiceImpl(
|
|||||||
OnlineInfoGetParam.Scope.FIVE_YEARS -> minusYears(5)
|
OnlineInfoGetParam.Scope.FIVE_YEARS -> minusYears(5)
|
||||||
else -> minusWeeks(1)
|
else -> minusWeeks(1)
|
||||||
}
|
}
|
||||||
},
|
}.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
|
||||||
LocalDateTime.now(ZoneOffset.UTC)
|
LocalDateTime.now(ZoneOffset.UTC).plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
|
||||||
)
|
)
|
||||||
).map {
|
).map {
|
||||||
OnlineInfoVo.HistoryVo(
|
OnlineInfoVo.HistoryVo(
|
||||||
@@ -187,4 +194,35 @@ class StatisticServiceImpl(
|
|||||||
history = history
|
history = history
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun active(activeInfoGetParam: ActiveInfoGetParam?): ActiveInfoVo {
|
||||||
|
fun getHistory(event: String) = eventLogService.listMaps(
|
||||||
|
QueryWrapper<EventLog?>().select("strftime('%Y-%m-%d', operate_time) time, count(distinct operate_user_id) count")
|
||||||
|
.eq("event", event).groupBy("time").between(
|
||||||
|
activeInfoGetParam?.scope != ActiveInfoGetParam.Scope.ALL,
|
||||||
|
"operate_time",
|
||||||
|
LocalDateTime.now(ZoneOffset.UTC).run {
|
||||||
|
when (activeInfoGetParam?.scope) {
|
||||||
|
ActiveInfoGetParam.Scope.MONTH -> minusMonths(1)
|
||||||
|
ActiveInfoGetParam.Scope.QUARTER -> minusMonths(3)
|
||||||
|
ActiveInfoGetParam.Scope.YEAR -> minusYears(1)
|
||||||
|
ActiveInfoGetParam.Scope.TWO_YEARS -> minusYears(2)
|
||||||
|
ActiveInfoGetParam.Scope.THREE_YEARS -> minusYears(3)
|
||||||
|
ActiveInfoGetParam.Scope.FIVE_YEARS -> minusYears(5)
|
||||||
|
else -> minusWeeks(1)
|
||||||
|
}
|
||||||
|
}.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
|
||||||
|
LocalDateTime.now(ZoneOffset.UTC).plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
|
||||||
|
)
|
||||||
|
).map {
|
||||||
|
ActiveInfoVo.HistoryVo(
|
||||||
|
LocalDate.parse(
|
||||||
|
it["time"] as String,
|
||||||
|
DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||||
|
), it["count"] as Int
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActiveInfoVo(getHistory("REGISTER"), getHistory("LOGIN"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package top.fatweb.api.vo.permission
|
package top.fatweb.api.vo.permission
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@@ -22,6 +24,19 @@ data class LoginVo(
|
|||||||
example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTllYjFkYmVmZDQ0OWRkOThlOGNjNzZlNzZkMDgyNSIsInN1YiI6IjE3MDk5ODYwNTg2Nzk5NzU5MzgiLCJpc3MiOiJGYXRXZWIiLCJpYXQiOjE2OTY1MjgxMTcsImV4cCI6MTY5NjUzNTMxN30.U2ZsyrGk7NbsP-DJfdz9xgWSfect5r2iKQnlEsscAA8"
|
example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTllYjFkYmVmZDQ0OWRkOThlOGNjNzZlNzZkMDgyNSIsInN1YiI6IjE3MDk5ODYwNTg2Nzk5NzU5MzgiLCJpc3MiOiJGYXRXZWIiLCJpYXQiOjE2OTY1MjgxMTcsImV4cCI6MTY5NjUzNTMxN30.U2ZsyrGk7NbsP-DJfdz9xgWSfect5r2iKQnlEsscAA8"
|
||||||
) val token: String,
|
) val token: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User ID
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Schema(
|
||||||
|
description = "User ID",
|
||||||
|
example = "1709986058679975938"
|
||||||
|
)
|
||||||
|
@JsonSerialize(using = ToStringSerializer::class)
|
||||||
|
val userId: Long?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last login time
|
* Last login time
|
||||||
*
|
*
|
||||||
|
|||||||
48
src/main/kotlin/top/fatweb/api/vo/system/ActiveInfoVo.kt
Normal file
48
src/main/kotlin/top/fatweb/api/vo/system/ActiveInfoVo.kt
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package top.fatweb.api.vo.system
|
||||||
|
|
||||||
|
import top.fatweb.api.vo.system.ActiveInfoVo.HistoryVo
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active information value object
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
data class ActiveInfoVo(
|
||||||
|
/**
|
||||||
|
* Register user number history
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
val registerHistory: List<HistoryVo>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login user number history
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
* @see HistoryVo
|
||||||
|
*/
|
||||||
|
val loginHistory: List<HistoryVo>
|
||||||
|
) {
|
||||||
|
data class HistoryVo(
|
||||||
|
/**
|
||||||
|
* Time
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
* @see LocalDate
|
||||||
|
*/
|
||||||
|
val time: LocalDate,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count
|
||||||
|
*
|
||||||
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
val count: Int
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package top.fatweb.api.vo.system
|
package top.fatweb.api.vo.system
|
||||||
|
|
||||||
|
import top.fatweb.api.vo.system.OnlineInfoVo.HistoryVo
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user