Complete core functions #9

Merged
FatttSnake merged 171 commits from FatttSnake into dev 2024-02-23 11:56:35 +08:00
6 changed files with 145 additions and 34 deletions
Showing only changes of commit 1d77caf390 - Show all commits

View File

@@ -28,7 +28,7 @@ create table t_sys_log
id bigint not null,
log_type varchar(50) not null comment '日志类型',
operate_user_id bigint not null comment '操作用户',
operate_time datetime not null default (utc_timestamp()) comment '操作时间',
operate_time datetime(3) not null default (utc_timestamp()) comment '操作时间',
request_uri varchar(500) default null comment '请求 URI',
request_method varchar(10) default null comment '请求方式',
request_params text comment '请求参数',
@@ -36,12 +36,10 @@ create table t_sys_log
request_server_address varchar(50) not null comment '请求服务器地址',
is_exception char(1) default null comment '是否异常',
exception_info text comment '异常信息',
start_time datetime not null comment '开始时间',
end_time datetime not null comment '结束时间',
execute_time int default null comment '执行时间',
start_time datetime(3) not null comment '开始时间',
end_time datetime(3) not null comment '结束时间',
execute_time bigint default null comment '执行时间',
user_agent varchar(500) default null comment '用户代理',
device_name varchar(100) default null comment '操作系统',
browser_name varchar(100) default null comment '浏览器名称',
primary key (id) using btree,
key idx_sys_log_log_type (log_type) using btree,
key idx_sys_log_operate_user_id (operate_user_id) using btree,

View File

@@ -0,0 +1,15 @@
package top.fatweb.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.api.interceptor.SysLogInterceptor
@Configuration
class SysLogConfig(
private val sysLogInterceptor: SysLogInterceptor
) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(sysLogInterceptor).addPathPatterns("/**").excludePathPatterns("/error/thrown")
}
}

View File

@@ -1,10 +1,10 @@
package top.fatweb.api.entity;
package top.fatweb.api.entity
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import java.io.Serializable
import java.time.LocalDateTime
/**
* <p>
@@ -96,7 +96,7 @@ class SysLog : Serializable {
* 执行时间
*/
@TableField("execute_time")
var executeTime: Int? = null
var executeTime: Long? = null
/**
* 用户代理
@@ -104,19 +104,7 @@ class SysLog : Serializable {
@TableField("user_agent")
var userAgent: String? = null
/**
* 操作系统
*/
@TableField("device_name")
var deviceName: String? = null
/**
* 浏览器名称
*/
@TableField("browser_name")
var browserName: 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, isException=$isException, exceptionInfo=$exceptionInfo, startTime=$startTime, endTime=$endTime, executeTime=$executeTime, userAgent=$userAgent, deviceName=$deviceName, browserName=$browserName)"
return "SysLog(id=$id, logType=$logType, operateUserId=$operateUserId, operateTime=$operateTime, requestUri=$requestUri, requestMethod=$requestMethod, requestParams=$requestParams, requestIp=$requestIp, requestServerAddress=$requestServerAddress, isException=$isException, exceptionInfo=$exceptionInfo, startTime=$startTime, endTime=$endTime, executeTime=$executeTime, userAgent=$userAgent)"
}
}

View File

@@ -0,0 +1,110 @@
package top.fatweb.api.interceptor
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.Logger
import org.slf4j.LoggerFactory
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.api.entity.SysLog
import top.fatweb.api.entity.common.ResponseResult
import top.fatweb.api.service.ISysLogService
import top.fatweb.api.util.WebUtil
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
@ControllerAdvice
class SysLogInterceptor(
val customThreadPoolTaskExecutor: Executor, val sysLogService: ISysLogService
) : HandlerInterceptor, ResponseBodyAdvice<Any> {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
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)
logger.info("开始计时: {} URI: {} IP: {}", sysLog.startTime, sysLog.requestUri, sysLog.requestIp)
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 = "INFO"
isException = "N"
}
} else {
sysLog.apply {
logType = "ERROR"
isException = "Y"
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)
return body
}
}

View File

@@ -10,6 +10,7 @@ import org.springframework.stereotype.Service
import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.entity.permission.LoginUser
import top.fatweb.api.entity.permission.User
import top.fatweb.api.exception.TokenHasExpiredException
import top.fatweb.api.service.IUserService
import top.fatweb.api.service.permission.IAuthenticationService
import top.fatweb.api.util.JwtUtil
@@ -56,10 +57,11 @@ class AuthenticationServiceImpl(
return LoginVo(jwt, loginUser.user.lastLoginTime, loginUser.user.lastLoginIp)
}
override fun logout(token: String): Boolean =
redisUtil.delObject("${SecurityConstants.jwtIssuer}_login:" + token)
override fun logout(token: String): Boolean = redisUtil.delObject("${SecurityConstants.jwtIssuer}_login:" + token)
override fun renewToken(token: String): TokenVo {
val loginUser = WebUtil.getLoginUser() ?: let { throw TokenHasExpiredException() }
val oldRedisKey = "${SecurityConstants.jwtIssuer}_login:" + token
redisUtil.delObject(oldRedisKey)
val jwt = JwtUtil.createJwt(WebUtil.getLoginUserId().toString())
@@ -70,10 +72,7 @@ class AuthenticationServiceImpl(
val redisKey = "${SecurityConstants.jwtIssuer}_login:" + jwt
redisUtil.setObject(
redisKey,
WebUtil.getLoginUser(),
SecurityConstants.redisTtl,
SecurityConstants.redisTtlUnit
redisKey, loginUser, SecurityConstants.redisTtl, SecurityConstants.redisTtlUnit
)
return TokenVo(jwt)

View File

@@ -6,9 +6,10 @@ import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.entity.permission.LoginUser
object WebUtil {
fun getLoginUser() = SecurityContextHolder.getContext().authentication.principal as LoginUser
fun getLoginUser() = if (SecurityContextHolder.getContext().authentication.principal is String) null
else SecurityContextHolder.getContext().authentication.principal as LoginUser
fun getLoginUserId() = getLoginUser().user.id
fun getLoginUserId() = getLoginUser()?.user?.id
fun getToken(tokenWithPrefix: String) = tokenWithPrefix.removePrefix(SecurityConstants.tokenPrefix)