feat(auth): 新增登录日志模块并记录登录失败

- 新增 SystemLoginLog 实体、Mapper、Service 及 XML 配置
- UserController 登录接口补充异常捕获与失败日志记录
- 删除旧压缩日志,新增最新日志文件
- 补充 AGENTS.md 使用说明文档
This commit is contained in:
2026-03-10 11:49:45 +08:00
parent e754fef4da
commit e56fc03099
8 changed files with 489 additions and 9 deletions

View File

@@ -1,18 +1,11 @@
package com.yupi.springbootinit.controller;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.model.dto.user.SystemUsersDTO;
import com.yupi.springbootinit.model.entity.SystemUsers;
import com.yupi.springbootinit.model.enums.CommonStatusEnum;
import com.yupi.springbootinit.model.enums.LoginSceneEnum;
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
import com.yupi.springbootinit.service.SystemUsersService;
import com.yupi.springbootinit.service.SystemLoginLogService;
import com.yupi.springbootinit.service.impl.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@@ -34,10 +27,20 @@ public class UserController {
@Resource
private LoginService loginService;
@Resource
private SystemLoginLogService systemLoginLogService;
// 用户登陆接口
@PostMapping("doLogin")
public BaseResponse<SystemUsersVO> doLogin(@RequestBody SystemUsersDTO usersDTO) {
return ResultUtils.success(loginService.login(LoginSceneEnum.HOST, usersDTO));
try {
SystemUsersVO usersVO = loginService.login(LoginSceneEnum.HOST, usersDTO);
systemLoginLogService.recordDoLoginLog(usersDTO, usersVO, true);
return ResultUtils.success(usersVO);
} catch (RuntimeException exception) {
recordFailedDoLoginLog(usersDTO, exception);
throw exception;
}
}
@@ -73,4 +76,12 @@ public class UserController {
return ResultUtils.success(loginService.bigBrotherLogout(usersDTO));
}
private void recordFailedDoLoginLog(SystemUsersDTO usersDTO, RuntimeException exception) {
try {
systemLoginLogService.recordDoLoginLog(usersDTO, null, false);
} catch (RuntimeException logException) {
logException.addSuppressed(exception);
throw logException;
}
}
}

View File

@@ -0,0 +1,12 @@
package com.yupi.springbootinit.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yupi.springbootinit.model.entity.SystemLoginLog;
/*
* @author: ziin
* @date: 2026/3/10 11:32
*/
public interface SystemLoginLogMapper extends BaseMapper<SystemLoginLog> {
}

View File

@@ -0,0 +1,129 @@
package com.yupi.springbootinit.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
import lombok.Data;
/*
* @author: ziin
* @date: 2026/3/10 11:32
*/
/**
* 系统访问记录
*/
@ApiModel(description="系统访问记录")
@Data
@TableName(value = "system_login_log")
public class SystemLoginLog {
/**
* 访问ID
*/
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value="访问ID")
private Long id;
/**
* 日志类型
*/
@TableField(value = "log_type")
@ApiModelProperty(value="日志类型")
private Long logType;
/**
* 链路追踪编号
*/
@TableField(value = "trace_id")
@ApiModelProperty(value="链路追踪编号")
private String traceId;
/**
* 用户编号
*/
@TableField(value = "user_id")
@ApiModelProperty(value="用户编号")
private Long userId;
/**
* 用户类型
*/
@TableField(value = "user_type")
@ApiModelProperty(value="用户类型")
private Byte userType;
/**
* 用户账号
*/
@TableField(value = "username")
@ApiModelProperty(value="用户账号")
private String username;
/**
* 登陆结果
*/
@TableField(value = "`result`")
@ApiModelProperty(value="登陆结果")
private Byte result;
/**
* 用户 IP
*/
@TableField(value = "user_ip")
@ApiModelProperty(value="用户 IP")
private String userIp;
/**
* 浏览器 UA
*/
@TableField(value = "user_agent")
@ApiModelProperty(value="浏览器 UA")
private String userAgent;
/**
* 创建者
*/
@TableField(value = "creator")
@ApiModelProperty(value="创建者")
private String creator;
/**
* 创建时间
*/
@TableField(value = "create_time")
@ApiModelProperty(value="创建时间")
private Date createTime;
/**
* 更新者
*/
@TableField(value = "updater")
@ApiModelProperty(value="更新者")
private String updater;
/**
* 更新时间
*/
@TableField(value = "update_time")
@ApiModelProperty(value="更新时间")
private Date updateTime;
/**
* 是否删除
*/
@TableField(value = "deleted")
@ApiModelProperty(value="是否删除")
private Boolean deleted;
/**
* 租户编号
*/
@TableField(value = "tenant_id")
@ApiModelProperty(value="租户编号")
private Long tenantId;
}

View File

@@ -0,0 +1,22 @@
package com.yupi.springbootinit.service;
import com.yupi.springbootinit.model.entity.SystemLoginLog;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yupi.springbootinit.model.dto.user.SystemUsersDTO;
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
/*
* @author: ziin
* @date: 2026/3/10 11:32
*/
public interface SystemLoginLogService extends IService<SystemLoginLog>{
/**
* 记录主播端 doLogin 登录日志
*
* @param usersDTO 登录请求参数
* @param usersVO 登录成功后的用户信息,失败时为 null
* @param success 是否登录成功
*/
void recordDoLoginLog(SystemUsersDTO usersDTO, SystemUsersVO usersVO, boolean success);
}

View File

@@ -0,0 +1,83 @@
package com.yupi.springbootinit.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.mapper.SystemLoginLogMapper;
import com.yupi.springbootinit.model.dto.user.SystemUsersDTO;
import com.yupi.springbootinit.model.entity.SystemLoginLog;
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
import com.yupi.springbootinit.service.SystemLoginLogService;
import com.yupi.springbootinit.utils.NetUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.UUID;
/*
* @author: ziin
* @date: 2026/3/10 11:32
*/
@Service
public class SystemLoginLogServiceImpl extends ServiceImpl<SystemLoginLogMapper, SystemLoginLog> implements SystemLoginLogService{
private static final Long HOST_LOGIN_LOG_TYPE = 1L;
private static final Byte LOGIN_SUCCESS = 1;
private static final Byte LOGIN_FAILURE = 0;
private static final String USER_AGENT_HEADER = "User-Agent";
private static final String TRACE_ID_HEADER = "X-Trace-Id";
private static final String SYSTEM_OPERATOR = "system";
@Override
public void recordDoLoginLog(SystemUsersDTO usersDTO, SystemUsersVO usersVO, boolean success) {
HttpServletRequest request = getCurrentRequest();
Date now = new Date();
SystemLoginLog loginLog = new SystemLoginLog();
loginLog.setLogType(HOST_LOGIN_LOG_TYPE);
loginLog.setTraceId(resolveTraceId(request));
loginLog.setUserId(usersVO == null ? null : usersVO.getId());
loginLog.setUsername(resolveUsername(usersDTO, usersVO));
loginLog.setResult(success ? LOGIN_SUCCESS : LOGIN_FAILURE);
loginLog.setUserIp(NetUtils.getIpAddress(request));
loginLog.setUserAgent(request.getHeader(USER_AGENT_HEADER));
loginLog.setCreator(SYSTEM_OPERATOR);
loginLog.setCreateTime(now);
loginLog.setUpdater(SYSTEM_OPERATOR);
loginLog.setUpdateTime(now);
loginLog.setDeleted(Boolean.FALSE);
loginLog.setTenantId(resolveTenantId(usersDTO, usersVO));
boolean saved = save(loginLog);
if (!saved) {
throw new IllegalStateException("保存 doLogin 登录日志失败");
}
}
private HttpServletRequest getCurrentRequest() {
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attributes.getRequest();
}
private String resolveTraceId(HttpServletRequest request) {
String traceId = request.getHeader(TRACE_ID_HEADER);
if (traceId == null || traceId.isBlank()) {
return UUID.randomUUID().toString();
}
return traceId;
}
private String resolveUsername(SystemUsersDTO usersDTO, SystemUsersVO usersVO) {
if (usersVO != null && usersVO.getUsername() != null) {
return usersVO.getUsername();
}
return usersDTO.getUsername();
}
private Long resolveTenantId(SystemUsersDTO usersDTO, SystemUsersVO usersVO) {
if (usersVO != null && usersVO.getTenantId() != null) {
return usersVO.getTenantId();
}
return usersDTO.getTenantId();
}
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yupi.springbootinit.mapper.SystemLoginLogMapper">
<resultMap id="BaseResultMap" type="com.yupi.springbootinit.model.entity.SystemLoginLog">
<!--@mbg.generated-->
<!--@Table system_login_log-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="log_type" jdbcType="BIGINT" property="logType" />
<result column="trace_id" jdbcType="VARCHAR" property="traceId" />
<result column="user_id" jdbcType="BIGINT" property="userId" />
<result column="user_type" jdbcType="TINYINT" property="userType" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="result" jdbcType="TINYINT" property="result" />
<result column="user_ip" jdbcType="VARCHAR" property="userIp" />
<result column="user_agent" jdbcType="VARCHAR" property="userAgent" />
<result column="creator" jdbcType="VARCHAR" property="creator" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="updater" jdbcType="VARCHAR" property="updater" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
<result column="deleted" jdbcType="BIT" property="deleted" />
<result column="tenant_id" jdbcType="BIGINT" property="tenantId" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, log_type, trace_id, user_id, user_type, username, `result`, user_ip, user_agent,
creator, create_time, updater, update_time, deleted, tenant_id
</sql>
</mapper>