Add active information api

This commit is contained in:
2023-12-19 16:52:53 +08:00
parent 7e792ca35d
commit b2b4ac5302
10 changed files with 169 additions and 12 deletions

View File

@@ -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)

View File

@@ -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))
} }

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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)

View File

@@ -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
} }

View File

@@ -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"))
}
} }

View File

@@ -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
* *

View 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
)
}

View File

@@ -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
/** /**
@@ -26,7 +27,7 @@ data class OnlineInfoVo(
*/ */
val history: List<HistoryVo> val history: List<HistoryVo>
) { ) {
data class HistoryVo ( data class HistoryVo(
/** /**
* Time * Time
* *