🎨 新增用户登录日志记录

This commit is contained in:
2025-09-08 01:48:36 +08:00
parent df46c7ab29
commit a65266d033
8 changed files with 140 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
common "git.echol.cn/loser/lckt/model/common/request"
user2 "git.echol.cn/loser/lckt/model/user"
"gorm.io/gorm"
"strconv"
"time"
@@ -67,6 +68,24 @@ func (*AppUserApi) Login(ctx *gin.Context) {
return
}
// 添加登录日志
loginLog := user2.LoginLog{
UserId: user.ID,
UserName: user.NickName,
Phone: user.Phone,
Ip: ctx.ClientIP(),
Address: utils.GetIPAdcode(ctx.ClientIP()),
Device: ctx.Request.Header.Get("sec-ch-ua-platform"),
UserAgent: ctx.Request.UserAgent(),
Mode: "微信登录",
LoginTime: time.Now().Format("2006-01-02 15:04:05"),
}
err = global.GVA_DB.Create(&loginLog).Error
if err != nil {
global.GVA_LOG.Error("添加登录日志失败!", zap.Error(err))
}
if _, err = global.GVA_REDIS.Get(ctx, strconv.Itoa(int(user.ID))).Result(); errors.Is(err, redis.Nil) {
// 此处过期时间等于jwt过期时间
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
@@ -132,6 +151,24 @@ func (*AppUserApi) WechatLogin(ctx *gin.Context) {
}
user_jwt.SetToken(ctx, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
// 添加登录日志
loginLog := user2.LoginLog{
UserId: user.ID,
UserName: user.NickName,
Phone: user.Phone,
Ip: ctx.ClientIP(),
Address: utils.GetIPAdcode(ctx.ClientIP()),
Device: ctx.Request.Header.Get("sec-ch-ua-platform"),
UserAgent: ctx.Request.UserAgent(),
Mode: "微信登录",
LoginTime: time.Now().Format("2006-01-02 15:04:05"),
}
err = global.GVA_DB.Create(&loginLog).Error
if err != nil {
global.GVA_LOG.Error("添加登录日志失败!", zap.Error(err))
}
r.OkWithDetailed(gin.H{
"User": user,
"Token": token,

View File

@@ -171,3 +171,28 @@ func (a *UserApi) UpdateTeacherApplyStatus(context *gin.Context) {
}
r.OkWithMessage("更新教师申请状态成功", context)
}
// GetLoginLog 获取用户登录日志
func (a *UserApi) GetLoginLog(context *gin.Context) {
var p request.GetUserListReq
if err := context.ShouldBind(&p); err != nil {
global.GVA_LOG.Error("参数错误,获取登录日志失败", zap.Error(err))
r.FailWithMessage("参数错误,获取登录日志失败", context)
return
}
logs, total, err := userService.GetLoginLog(p)
if err != nil {
global.GVA_LOG.Error("获取登录日志失败", zap.Error(err))
r.FailWithMessage("获取登录日志失败", context)
return
}
r.OkWithDetailed(
r.PageResult{
List: logs,
Total: total,
Page: p.Page,
PageSize: p.PageSize,
}, "获取登录日志成功", context)
}

View File

@@ -83,6 +83,7 @@ func RegisterTables() {
app.Follow{},
app.TeacherVip{},
app.UserTeacherVip{},
user.LoginLog{},
)
if err != nil {
global.GVA_LOG.Error("register table failed", zap.Error(err))

22
model/user/login_log.go Normal file
View File

@@ -0,0 +1,22 @@
package user
import "git.echol.cn/loser/lckt/global"
type LoginLog struct {
global.GVA_MODEL
UserId uint `json:"user_id" gorm:"comment:用户ID;index"`
UserName string `json:"user_name" gorm:"comment:用户登录名"`
Phone string `json:"phone" gorm:"comment:用户手机号"`
Ip string `json:"ip" gorm:"comment:登录IP"`
Address string `json:"address" gorm:"comment:登录地址"`
UserAgent string `json:"user_agent" gorm:"comment:用户代理"`
// 登录设备
Device string `json:"device" gorm:"comment:登录设备"`
// 登录方式
Mode string `json:"mode" gorm:"comment:登录方式"`
LoginTime string `json:"login_time" gorm:"comment:登录时间"`
}
func (LoginLog) TableName() string {
return "user_login_log"
}

View File

@@ -43,6 +43,7 @@ type BindPhoneReq struct {
type GetUserListReq struct {
request.PageInfo
UserId uint `json:"user_id" form:"user_id"`
Type int `json:"type" form:"type"`
UserLabel string `json:"user_label" form:"user_label" `
Status int `json:"status" form:"status"`

View File

@@ -20,5 +20,6 @@ func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup, PublicRouter *gin.R
userRouter.GET("/teachers", userApi.GetTeachers) // 获取教师列表
userRouter.GET("/teacherApplyList", userApi.GetTeacherApplyList) // 获取教师申请列表
userRouter.PUT("/teacherApply/status", userApi.UpdateTeacherApplyStatus) // 更新教师申请状态
userRouter.GET("/login/log", userApi.GetLoginLog) // 获取用户登录日志
}
}

View File

@@ -248,3 +248,27 @@ func (u *UserService) SetUserVip(p request.SetUserVipReq) error {
}
return nil
}
func (u *UserService) GetLoginLog(p request.GetUserListReq) (list []user.LoginLog, total int64, err error) {
limit := p.PageSize
offset := (p.Page - 1) * p.PageSize
db := global.GVA_DB.Model(&user.LoginLog{})
if p.UserId != 0 {
db = db.Where("user_id = ?", p.UserId)
}
if p.Name != "" {
db = db.Where("user_name LIKE ?", "%"+p.Name+"%")
}
err = db.Count(&total).Error
if err != nil {
global.GVA_LOG.Error("查询登录日志总数失败", zap.Error(err))
return nil, 0, err
}
err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&list).Error
if err != nil {
global.GVA_LOG.Error("查询登录日志列表失败", zap.Error(err))
return nil, 0, err
}
return
}

29
utils/ip.go Normal file
View File

@@ -0,0 +1,29 @@
package utils
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type ipAdcodeResp struct {
Adcode struct {
O string `json:"o"`
} `json:"adcode"`
}
func GetIPAdcode(ip string) string {
url := fmt.Sprintf("https://api.vore.top/api/IPdata?ip=%s", ip)
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(url)
if err != nil {
return ""
}
defer resp.Body.Close()
var result ipAdcodeResp
if err = json.NewDecoder(resp.Body).Decode(&result); err != nil {
return ""
}
return result.Adcode.O
}