diff --git a/src/main/kotlin/top/fatweb/api/aop/EventLogAspect.kt b/src/main/kotlin/top/fatweb/api/aop/EventLogAspect.kt index 44c3a54..9d1fe2a 100644 --- a/src/main/kotlin/top/fatweb/api/aop/EventLogAspect.kt +++ b/src/main/kotlin/top/fatweb/api/aop/EventLogAspect.kt @@ -12,6 +12,7 @@ import top.fatweb.api.annotation.EventLogRecord import top.fatweb.api.entity.system.EventLog import top.fatweb.api.service.system.IEventLogService import top.fatweb.api.util.WebUtil +import top.fatweb.api.vo.permission.LoginVo /** * Event log record aspect @@ -27,16 +28,18 @@ class EventLogAspect( private val logger: Logger = LoggerFactory.getLogger(this::class.java) @Pointcut("@annotation(top.fatweb.api.annotation.EventLogRecord)") - fun eventLogPointcut() {} + fun eventLogPointcut() { + } - @AfterReturning("eventLogPointcut()") - fun doAfter(joinPoint: JoinPoint) { + @AfterReturning(value = "eventLogPointcut()", returning = "retValue") + fun doAfter(joinPoint: JoinPoint, retValue: Any) { val annotation = (joinPoint.signature as MethodSignature).method.getAnnotation(EventLogRecord::class.java) try { eventLogService.save(EventLog().apply { this.event = annotation.event - operateUserId = WebUtil.getLoginUserId() ?: -1 + operateUserId = WebUtil.getLoginUserId() + ?: if (retValue is LoginVo) retValue.userId else -1 }) } catch (e: Exception) { logger.error("Cannot record event!!!", e) diff --git a/src/main/kotlin/top/fatweb/api/controller/system/StatisticController.kt b/src/main/kotlin/top/fatweb/api/controller/system/StatisticController.kt index 9aff799..0b207db 100644 --- a/src/main/kotlin/top/fatweb/api/controller/system/StatisticController.kt +++ b/src/main/kotlin/top/fatweb/api/controller/system/StatisticController.kt @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.Operation import org.springframework.web.bind.annotation.GetMapping import top.fatweb.api.annotation.BaseController 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.service.system.IStatisticService import top.fatweb.api.vo.system.* @@ -72,7 +73,7 @@ class StatisticController( fun storage(): ResponseResult = ResponseResult.success(data = statisticService.storage()) /** - * Get the number of online users information + * Get the history of online users information * * @author FatttSnake, fatttsnake@gmail.com * @since 1.0.0 @@ -81,4 +82,15 @@ class StatisticController( @GetMapping("/online") fun online(onlineInfoGetParam: OnlineInfoGetParam?): ResponseResult = 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 = + ResponseResult.success(data = statisticService.active(activeInfoGetParam)) } \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/api/interceptor/SysLogInterceptor.kt b/src/main/kotlin/top/fatweb/api/interceptor/SysLogInterceptor.kt index d424d3c..ab10b5b 100644 --- a/src/main/kotlin/top/fatweb/api/interceptor/SysLogInterceptor.kt +++ b/src/main/kotlin/top/fatweb/api/interceptor/SysLogInterceptor.kt @@ -16,6 +16,7 @@ import top.fatweb.api.entity.common.ResponseResult import top.fatweb.api.entity.system.SysLog import top.fatweb.api.service.system.ISysLogService import top.fatweb.api.util.WebUtil +import top.fatweb.api.vo.permission.LoginVo import java.net.URI import java.time.LocalDateTime import java.time.ZoneOffset @@ -33,7 +34,8 @@ import java.util.concurrent.Executor */ @ControllerAdvice class SysLogInterceptor( - @Qualifier("applicationTaskExecutor") private val customThreadPoolTaskExecutor: Executor, private val sysLogService: ISysLogService + @Qualifier("applicationTaskExecutor") private val customThreadPoolTaskExecutor: Executor, + private val sysLogService: ISysLogService ) : HandlerInterceptor, ResponseBodyAdvice { private val sysLogThreadLocal = ThreadLocal() private val resultThreadLocal = ThreadLocal() @@ -77,6 +79,9 @@ class SysLogInterceptor( } ?: SysLog.LogType.INFO exception = 0 } + if (result.data is LoginVo) { + sysLog.operateUserId = result.data.userId ?: -1 + } } else { sysLog.apply { logType = SysLog.LogType.ERROR diff --git a/src/main/kotlin/top/fatweb/api/param/system/ActiveInfoGetParam.kt b/src/main/kotlin/top/fatweb/api/param/system/ActiveInfoGetParam.kt new file mode 100644 index 0000000..7681e52 --- /dev/null +++ b/src/main/kotlin/top/fatweb/api/param/system/ActiveInfoGetParam.kt @@ -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") + } +} diff --git a/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt b/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt index a9da6e0..2e4f5cb 100644 --- a/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt +++ b/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt @@ -70,7 +70,7 @@ class AuthenticationServiceImpl( val redisKey = "${SecurityProperties.jwtIssuer}_login_${userId}:" + jwt 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) diff --git a/src/main/kotlin/top/fatweb/api/service/system/IStatisticService.kt b/src/main/kotlin/top/fatweb/api/service/system/IStatisticService.kt index 750b592..a84c741 100644 --- a/src/main/kotlin/top/fatweb/api/service/system/IStatisticService.kt +++ b/src/main/kotlin/top/fatweb/api/service/system/IStatisticService.kt @@ -1,5 +1,6 @@ package top.fatweb.api.service.system +import top.fatweb.api.param.system.ActiveInfoGetParam import top.fatweb.api.param.system.OnlineInfoGetParam import top.fatweb.api.vo.system.* @@ -51,10 +52,18 @@ interface IStatisticService { fun storage(): StorageInfoVo /** - * Get the number of online users information + * Get the history of online users information * * @author FatttSnake, fatttsnake@gmail.com * @since 1.0.0 */ 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 } \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/api/service/system/impl/StatisticServiceImpl.kt b/src/main/kotlin/top/fatweb/api/service/system/impl/StatisticServiceImpl.kt index c8aec6f..cf93c3a 100644 --- a/src/main/kotlin/top/fatweb/api/service/system/impl/StatisticServiceImpl.kt +++ b/src/main/kotlin/top/fatweb/api/service/system/impl/StatisticServiceImpl.kt @@ -1,20 +1,26 @@ package top.fatweb.api.service.system.impl +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper import org.springframework.stereotype.Service import oshi.SystemInfo import oshi.hardware.CentralProcessor +import top.fatweb.api.entity.system.EventLog 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.properties.SecurityProperties 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.IStatisticService import top.fatweb.api.util.ByteUtil import top.fatweb.api.util.RedisUtil import top.fatweb.api.vo.system.* +import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneOffset +import java.time.format.DateTimeFormatter import java.util.* import java.util.concurrent.TimeUnit @@ -27,7 +33,8 @@ import java.util.concurrent.TimeUnit @Service class StatisticServiceImpl( private val redisUtil: RedisUtil, - private val statisticLogService: IStatisticLogService + private val statisticLogService: IStatisticLogService, + private val eventLogService: IEventLogService ) : IStatisticService { private val systemProperties: Properties = System.getProperties() private val systemInfo: SystemInfo = SystemInfo() @@ -171,8 +178,8 @@ class StatisticServiceImpl( OnlineInfoGetParam.Scope.FIVE_YEARS -> minusYears(5) else -> minusWeeks(1) } - }, - LocalDateTime.now(ZoneOffset.UTC) + }.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + LocalDateTime.now(ZoneOffset.UTC).plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) ) ).map { OnlineInfoVo.HistoryVo( @@ -187,4 +194,35 @@ class StatisticServiceImpl( history = history ) } + + override fun active(activeInfoGetParam: ActiveInfoGetParam?): ActiveInfoVo { + fun getHistory(event: String) = eventLogService.listMaps( + QueryWrapper().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")) + } } \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/api/vo/permission/LoginVo.kt b/src/main/kotlin/top/fatweb/api/vo/permission/LoginVo.kt index 2e724ba..8b130eb 100644 --- a/src/main/kotlin/top/fatweb/api/vo/permission/LoginVo.kt +++ b/src/main/kotlin/top/fatweb/api/vo/permission/LoginVo.kt @@ -1,5 +1,7 @@ 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 java.time.LocalDateTime @@ -22,6 +24,19 @@ data class LoginVo( example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTllYjFkYmVmZDQ0OWRkOThlOGNjNzZlNzZkMDgyNSIsInN1YiI6IjE3MDk5ODYwNTg2Nzk5NzU5MzgiLCJpc3MiOiJGYXRXZWIiLCJpYXQiOjE2OTY1MjgxMTcsImV4cCI6MTY5NjUzNTMxN30.U2ZsyrGk7NbsP-DJfdz9xgWSfect5r2iKQnlEsscAA8" ) 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 * diff --git a/src/main/kotlin/top/fatweb/api/vo/system/ActiveInfoVo.kt b/src/main/kotlin/top/fatweb/api/vo/system/ActiveInfoVo.kt new file mode 100644 index 0000000..5aa59f7 --- /dev/null +++ b/src/main/kotlin/top/fatweb/api/vo/system/ActiveInfoVo.kt @@ -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, + + /** + * Login user number history + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HistoryVo + */ + val loginHistory: List +) { + 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 + ) +} diff --git a/src/main/kotlin/top/fatweb/api/vo/system/OnlineInfoVo.kt b/src/main/kotlin/top/fatweb/api/vo/system/OnlineInfoVo.kt index 982671e..d98ddf7 100644 --- a/src/main/kotlin/top/fatweb/api/vo/system/OnlineInfoVo.kt +++ b/src/main/kotlin/top/fatweb/api/vo/system/OnlineInfoVo.kt @@ -1,5 +1,6 @@ package top.fatweb.api.vo.system +import top.fatweb.api.vo.system.OnlineInfoVo.HistoryVo import java.time.LocalDateTime /** @@ -26,7 +27,7 @@ data class OnlineInfoVo( */ val history: List ) { - data class HistoryVo ( + data class HistoryVo( /** * Time *