feat: 添加日志管理和敏感词过滤功能

- 新增日志管理模块:操作日志、登录日志、数据变更日志
- 新增敏感词过滤器 (sanitizer)
- 新增日志异步写入管理器
- 新增日志定时清理服务
- 优化帖子相关服务和上传服务
This commit is contained in:
2026-03-15 02:25:10 +08:00
parent d3065b30cc
commit ebd9cb8b04
31 changed files with 2753 additions and 60 deletions

View File

@@ -0,0 +1,56 @@
package model
import (
"time"
)
// ChangeType 变更类型
type ChangeType string
const (
ChangePassword ChangeType = "password"
ChangePhone ChangeType = "phone"
ChangeEmail ChangeType = "email"
ChangeNickName ChangeType = "nickname"
ChangeAvatar ChangeType = "avatar"
ChangeProfile ChangeType = "profile"
ChangePrivacy ChangeType = "privacy"
ChangeDelete ChangeType = "delete"
ChangeExport ChangeType = "export"
ChangeWithdraw ChangeType = "withdraw"
)
// OperatorType 操作人类型
type OperatorType string
const (
OperatorTypeSelf OperatorType = "self"
OperatorTypeAdmin OperatorType = "admin"
OperatorTypeSystem OperatorType = "system"
)
// DataChangeLog 数据变更日志实体
type DataChangeLog struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
UserID string `json:"user_id" gorm:"type:varchar(36);index:idx_user"`
OperatorID string `json:"operator_id" gorm:"type:varchar(36);index:idx_operator"`
OperatorName string `json:"operator_name" gorm:"type:varchar(100)"`
ChangeType string `json:"change_type" gorm:"type:varchar(50);index:idx_change_type"`
TargetType string `json:"target_type" gorm:"type:varchar(50)"`
TargetID string `json:"target_id" gorm:"type:varchar(255)"`
FieldName string `json:"field_name" gorm:"type:varchar(50)"`
OldValue string `json:"old_value" gorm:"type:text"`
NewValue string `json:"new_value" gorm:"type:text"`
IP string `json:"ip" gorm:"type:varchar(45);index:idx_ip"`
IPLocation string `json:"ip_location" gorm:"type:varchar(100)"`
UserAgent string `json:"user_agent" gorm:"type:varchar(500)"`
Reason string `json:"reason" gorm:"type:varchar(500)"`
OperatorType string `json:"operator_type" gorm:"type:varchar(20);index:idx_operator_type"`
DeviceID string `json:"device_id" gorm:"type:varchar(100)"`
ServerIP string `json:"server_ip" gorm:"type:varchar(45)"`
CreatedAt time.Time `json:"created_at" gorm:"index:idx_created"`
}
func (DataChangeLog) TableName() string {
return "data_change_logs"
}

View File

@@ -0,0 +1,87 @@
package model
import (
"time"
)
// LoginEvent 登录事件
type LoginEvent string
const (
LoginEventLogin LoginEvent = "login"
LoginEventLogout LoginEvent = "logout"
LoginEventRefresh LoginEvent = "refresh"
LoginEventReset LoginEvent = "reset"
LoginEventFail LoginEvent = "fail"
)
// LoginResult 登录结果
type LoginResult string
const (
LoginResultSuccess LoginResult = "success"
LoginResultFail LoginResult = "fail"
)
// FailReason 失败原因
type FailReason string
const (
FailReasonUserNotFound FailReason = "user_not_found"
FailReasonWrongPassword FailReason = "wrong_password"
FailReasonAccountLocked FailReason = "account_locked"
FailReasonAccountBanned FailReason = "account_banned"
FailReasonRateLimit FailReason = "rate_limit"
FailReasonInvalidToken FailReason = "invalid_token"
FailReasonTokenExpired FailReason = "token_expired"
FailReasonInvalidCode FailReason = "invalid_code"
FailReasonCodeExpired FailReason = "code_expired"
FailReasonUnknown FailReason = "unknown"
)
// LoginType 登录类型
type LoginType string
const (
LoginTypePassword LoginType = "password"
LoginTypeSMS LoginType = "sms"
LoginTypeEmail LoginType = "email"
LoginTypeOAuth LoginType = "oauth"
)
// LoginMethod 登录方式
type LoginMethod string
const (
LoginMethodUsername LoginMethod = "username"
LoginMethodMobile LoginMethod = "mobile"
LoginMethodEmail LoginMethod = "email"
)
// LoginLog 登录日志实体
type LoginLog struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
UserID string `json:"user_id" gorm:"type:varchar(36);index:idx_user"`
UserName string `json:"user_name" gorm:"type:varchar(100)"`
NickName string `json:"nick_name" gorm:"type:varchar(100)"`
LoginType string `json:"login_type" gorm:"type:varchar(20);index:idx_login_type"`
LoginMethod string `json:"login_method" gorm:"type:varchar(20)"`
Event string `json:"event" gorm:"type:varchar(20);index:idx_event"`
IP string `json:"ip" gorm:"type:varchar(45);index:idx_ip"`
IPLocation string `json:"ip_location" gorm:"type:varchar(100)"`
UserAgent string `json:"user_agent" gorm:"type:varchar(500)"`
DeviceType string `json:"device_type" gorm:"type:varchar(20)"`
DeviceID string `json:"device_id" gorm:"type:varchar(100)"`
AppVersion string `json:"app_version" gorm:"type:varchar(50)"`
Result string `json:"result" gorm:"type:varchar(20);index:idx_result"`
FailReason string `json:"fail_reason" gorm:"type:varchar(200)"`
TokenID string `json:"token_id" gorm:"type:varchar(64)"`
ExpiredAt *time.Time `json:"expired_at"`
ServerIP string `json:"server_ip" gorm:"type:varchar(45)"`
ServerPort int `json:"server_port" gorm:"type:int"`
CreatedAt time.Time `json:"created_at" gorm:"index:idx_created"`
}
func (LoginLog) TableName() string {
return "login_logs"
}

View File

@@ -0,0 +1,86 @@
package model
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// OperationType 操作类型
type OperationType string
const (
OpPostCreate OperationType = "post:create"
OpPostUpdate OperationType = "post:update"
OpPostDelete OperationType = "post:delete"
OpCommentCreate OperationType = "comment:create"
OpCommentDelete OperationType = "comment:delete"
OpUserRegister OperationType = "user:register"
OpUserLogin OperationType = "user:login"
OpUserLogout OperationType = "user:logout"
OpPasswordChange OperationType = "password:change"
OpPasswordReset OperationType = "password:reset"
OpProfileUpdate OperationType = "profile:update"
OpAvatarUpload OperationType = "avatar:upload"
OpEmailBind OperationType = "email:bind"
OpPhoneBind OperationType = "phone:bind"
OpAccountDelete OperationType = "account:delete"
OpDataExport OperationType = "data:export"
OpDataWithdraw OperationType = "data:withdraw"
OpAdminBanUser OperationType = "admin:ban_user"
OpAdminUnbanUser OperationType = "admin:unban_user"
OpAdminDeletePost OperationType = "admin:delete_post"
OpAdminModifyRole OperationType = "admin:modify_role"
OpAdminMuteUser OperationType = "admin:mute_user"
OpAdminUnmuteUser OperationType = "admin:unmute_user"
)
// OperationLog 操作日志实体
type OperationLog struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
TraceID string `json:"trace_id" gorm:"type:varchar(64);index:idx_trace"`
UserID string `json:"user_id" gorm:"type:varchar(36);index:idx_user"`
UserName string `json:"user_name" gorm:"type:varchar(100)"`
NickName string `json:"nick_name" gorm:"type:varchar(100)"`
Operation string `json:"operation" gorm:"type:varchar(50);index:idx_operation"`
Module string `json:"module" gorm:"type:varchar(50)"`
TargetType string `json:"target_type" gorm:"type:varchar(50)"`
TargetID string `json:"target_id" gorm:"type:varchar(255);index:idx_target"`
Method string `json:"method" gorm:"type:varchar(10)"`
Path string `json:"path" gorm:"type:varchar(500)"`
QueryParams string `json:"query_params" gorm:"type:text"`
RequestBody string `json:"request_body" gorm:"type:text"`
ResponseCode int `json:"response_code" gorm:"index:idx_status"`
ResponseMsg string `json:"response_msg" gorm:"type:varchar(500)"`
IP string `json:"ip" gorm:"type:varchar(45);index:idx_ip"`
IPLocation string `json:"ip_location" gorm:"type:varchar(100)"`
UserAgent string `json:"user_agent" gorm:"type:varchar(500)"`
DeviceID string `json:"device_id" gorm:"type:varchar(100)"`
DeviceType string `json:"device_type" gorm:"type:varchar(20)"`
Duration int64 `json:"duration" gorm:"type:bigint"`
Referer string `json:"referer" gorm:"type:varchar(500)"`
Protocol string `json:"protocol" gorm:"type:varchar(10)"`
ServerIP string `json:"server_ip" gorm:"type:varchar(45)"`
ServerPort int `json:"server_port" gorm:"type:int"`
ClientPort int `json:"client_port" gorm:"type:int"`
OccurredAt time.Time `json:"occurred_at" gorm:"index:idx_created"`
Status string `json:"status" gorm:"type:varchar(20);index:idx_status"`
ErrorMsg string `json:"error_msg" gorm:"type:text"`
CreatedAt time.Time `json:"created_at" gorm:"index:idx_created"`
}
// BeforeCreate 创建前生成TraceID
func (ol *OperationLog) BeforeCreate(tx *gorm.DB) error {
if ol.TraceID == "" {
ol.TraceID = uuid.New().String()
}
if ol.OccurredAt.IsZero() {
ol.OccurredAt = time.Now()
}
return nil
}
func (OperationLog) TableName() string {
return "operation_logs"
}

View File

@@ -76,16 +76,18 @@ func (Post) TableName() string {
// PostImage 帖子图片
type PostImage struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
PostID string `json:"post_id" gorm:"type:varchar(36);index;not null"`
URL string `json:"url" gorm:"type:text;not null"`
ThumbnailURL string `json:"thumbnail_url" gorm:"type:text"`
Width int `json:"width" gorm:"default:0"`
Height int `json:"height" gorm:"default:0"`
Size int64 `json:"size" gorm:"default:0"` // 文件大小(字节)
MimeType string `json:"mime_type" gorm:"type:varchar(50)"`
SortOrder int `json:"sort_order" gorm:"default:0"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
PostID string `json:"post_id" gorm:"type:varchar(36);index;not null"`
URL string `json:"url" gorm:"type:text;not null"`
ThumbnailURL string `json:"thumbnail_url" gorm:"type:text"`
PreviewURL string `json:"preview_url" gorm:"type:text"` // 列表/网格预览图最大300px
PreviewURLLarge string `json:"preview_url_large" gorm:"type:text"` // 详情页预览图最大800px
Width int `json:"width" gorm:"default:0"`
Height int `json:"height" gorm:"default:0"`
Size int64 `json:"size" gorm:"default:0"` // 文件大小(字节)
MimeType string `json:"mime_type" gorm:"type:varchar(50)"`
SortOrder int `json:"sort_order" gorm:"default:0"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
}
// BeforeCreate 创建前生成UUID