Files
Logan.Li 16f1f7d65f feat(backend): unified response helpers, refresh token, security hardening
- new resp package (OK/Page/Fail/BadRequest/.../InternalErr): unified error
  envelope {detail, code?}, sanitized 500s (full error logged server-side,
  generic message to client - stops DB error leakage), pagination envelope
  {items,total,page,page_size}; migrate 200+ inline c.JSON error sites
- refresh token: 7d rotation tokens (SHA-256 hashed at rest), public
  POST /auth/refresh with single-use rotation, POST /auth/logout revokes
  all user refresh tokens (endpoint previously missing - frontend 404)
- security: gh-ost command line password masked in task status payload;
  Instance.MarshalJSON guarantees encrypted passwords never leave the API
  (removes reliance on per-handler manual masking); production (DEBUG=false)
  with default JWT secret now auto-generates and persists a random secret
- safe.Go recover wrapper for background goroutines (instance probe,
  gh-ost async)
- delete dead service layer (zero callers)
- fix pre-existing go vet unkeyed bson.E literals
2026-10-26 17:21:00 +08:00

547 lines
25 KiB
Go

package model
import (
"database/sql"
"encoding/json"
"time"
"gorm.io/gorm"
)
// ==================== User ====================
type User struct {
ID int `gorm:"primaryKey" json:"id"`
Username string `gorm:"size:50;uniqueIndex;not null" json:"username"`
DisplayName string `gorm:"size:100;not null" json:"display_name"`
Email *string `gorm:"size:100" json:"email"`
Phone *string `gorm:"size:20" json:"phone"`
HashedPassword string `gorm:"size:200;not null" json:"-"`
AuthSource string `gorm:"size:20;default:'local'" json:"auth_source"` // local / ldap
Role string `gorm:"size:20;not null" json:"role"` // admin, dba, developer, auditor
IsActive *bool `gorm:"default:true" json:"is_active"`
Preferences *string `gorm:"type:text" json:"preferences"` // JSON: {"theme":"system","login_style":"split"}
LastLogin *time.Time `json:"last_login"`
FailedLoginAttempts int `gorm:"default:0" json:"failed_login_attempts"`
LockedUntil *time.Time `json:"locked_until"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (User) TableName() string { return "users" }
// ==================== Instance ====================
type Instance struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
DBType string `gorm:"size:20;not null" json:"db_type"`
Host string `gorm:"size:200;not null" json:"host"`
Port int `gorm:"not null" json:"port"`
Username string `gorm:"size:100;not null" json:"username"`
Password string `gorm:"size:500;not null" json:"password,omitempty"`
Database *string `gorm:"size:100" json:"database"`
Env *string `gorm:"size:20" json:"env"`
Description *string `gorm:"type:text" json:"description"`
Status *string `gorm:"size:20" json:"status"`
Version *string `gorm:"size:50" json:"version"`
ClusterMode *bool `gorm:"default:false" json:"cluster_mode"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
AccessMode *string `gorm:"size:20;default:readwrite" json:"access_mode"`
Workflow *string `gorm:"type:text" json:"workflow"`
ExcludedDatabases *string `gorm:"type:text" json:"excluded_databases"`
MaskFields *string `gorm:"type:text" json:"mask_fields"`
GroupID *int `json:"group_id"`
WorkflowID *int `json:"workflow_id"`
}
func (Instance) TableName() string { return "instances" }
// MarshalJSON 保证实例密文密码在任何响应中都不出站,
// 消除各 handler 手动遮蔽遗漏的隐患(入站绑定不受影响)。
func (i Instance) MarshalJSON() ([]byte, error) {
type instanceAlias Instance
a := instanceAlias(i)
a.Password = ""
return json.Marshal(a)
}
// ==================== Ticket ====================
type Ticket struct {
ID int `gorm:"primaryKey" json:"id"`
Title string `gorm:"size:200;not null" json:"title"`
DBType string `gorm:"size:20;not null" json:"db_type"`
InstanceID int `gorm:"not null" json:"instance_id"`
TargetDatabase string `gorm:"size:100;not null" json:"target_database"`
SQLContent string `gorm:"type:text;not null" json:"sql_content"`
RollbackSQL *string `gorm:"type:text" json:"rollback_sql"`
Status *string `gorm:"size:20" json:"status"`
Priority *string `gorm:"size:10" json:"priority"`
ApplicantID int `gorm:"not null" json:"applicant_id"`
ApproverID *int `json:"approver_id"`
Reason *string `gorm:"type:text" json:"reason"`
AuditResult *string `gorm:"type:text" json:"audit_result"`
ExecuteResult *string `gorm:"type:text" json:"execute_result"`
CreateTime time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"create_time"`
ReviewTime *time.Time `json:"review_time"`
ExecuteTime *time.Time `json:"execute_time"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
TicketNo *string `gorm:"size:20" json:"ticket_no"`
ScheduledAt sql.NullTime `json:"scheduled_at"`
WorkflowID *int `json:"workflow_id"`
RiskLevel *string `gorm:"size:20;default:low" json:"risk_level"`
ExecStrategy *string `gorm:"size:20;default:stop_on_error" json:"exec_strategy"`
}
func (Ticket) TableName() string { return "tickets" }
// ==================== TicketApproval ====================
type TicketApproval struct {
ID int `gorm:"primaryKey" json:"id"`
TicketID int `gorm:"not null" json:"ticket_id"`
WorkflowID *int `json:"workflow_id"`
VersionID *int `json:"version_id"`
StepNumber int `gorm:"not null" json:"step_number"`
StepName *string `gorm:"size:100" json:"step_name"`
StepType *string `gorm:"size:20" json:"step_type"`
ApproverID *int `json:"approver_id"`
ApproverRole *string `gorm:"size:20" json:"approver_role"`
ActualApproverID *int `json:"actual_approver_id"`
Action *string `gorm:"size:20" json:"action"`
Comment *string `gorm:"type:text" json:"comment"`
TransferFrom *int `json:"transfer_from"`
TransferReason *string `gorm:"type:text" json:"transfer_reason"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
CompletedAt *time.Time `json:"completed_at"`
IsTimeout *bool `json:"is_timeout"`
TimeoutHandledAt *time.Time `json:"timeout_handled_at"`
}
func (TicketApproval) TableName() string { return "ticket_approvals" }
// ==================== TicketSQLItem ====================
type TicketSQLItem struct {
ID int `gorm:"primaryKey" json:"id"`
TicketID int `gorm:"not null" json:"ticket_id"`
Seq int `gorm:"not null" json:"seq"`
SQLText string `gorm:"type:text;not null" json:"sql_text"`
SQLType *string `gorm:"size:20" json:"sql_type"`
Status *string `gorm:"size:20;default:pending" json:"status"`
AffectedRows *int `gorm:"default:0" json:"affected_rows"`
DurationMs *int `gorm:"default:0" json:"duration_ms"`
ErrorMessage *string `gorm:"type:text" json:"error_message"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (TicketSQLItem) TableName() string { return "ticket_sql_items" }
// ==================== AuditRule ====================
type AuditRule struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Desc *string `gorm:"type:text" json:"description"`
RuleType string `gorm:"size:50;not null" json:"rule_type"`
Severity *string `gorm:"size:20" json:"severity"`
DBType *string `gorm:"size:20" json:"db_type"`
Pattern *string `gorm:"type:text" json:"pattern"`
IsEnabled *bool `json:"is_enabled"`
Message string `gorm:"size:500;not null" json:"message"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
Value *string `gorm:"size:255" json:"value"`
Code *string `gorm:"size:50" json:"code"`
ConfigJSON *string `gorm:"type:text;column:config_json" json:"config_json"`
}
func (AuditRule) TableName() string { return "audit_rules" }
// ==================== AuditLog ====================
type AuditLog struct {
ID int `gorm:"primaryKey" json:"id"`
UserID int `gorm:"not null" json:"user_id"`
Action string `gorm:"size:50;not null" json:"action"`
TargetType string `gorm:"size:50;not null" json:"target_type"`
TargetID *int `json:"target_id"`
Detail *string `gorm:"type:text" json:"detail"`
IPAddress *string `gorm:"size:50" json:"ip_address"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
Username *string `gorm:"size:50" json:"username"`
DBType *string `gorm:"size:20" json:"db_type"`
DatabaseName *string `gorm:"size:100" json:"database_name"`
InstanceName *string `gorm:"size:100" json:"instance_name"`
ExecutionTimeMs *int `json:"execution_time_ms"`
RowsAffected *int `json:"rows_affected"`
ResultStatus *string `gorm:"size:20" json:"result_status"`
QueryContent *string `gorm:"type:text" json:"query_content"`
UserAgent *string `gorm:"size:200" json:"user_agent"`
IsRisky *bool `gorm:"default:false" json:"is_risky"`
}
func (AuditLog) TableName() string { return "audit_logs" }
// ==================== QueryHistory ====================
type QueryHistory struct {
ID int `gorm:"primaryKey" json:"id"`
UserID int `gorm:"not null" json:"user_id"`
Username string `gorm:"size:100;not null" json:"username"`
InstanceID int `gorm:"not null" json:"instance_id"`
InstanceName string `gorm:"size:100;not null" json:"instance_name"`
DBType string `gorm:"size:20;not null" json:"db_type"`
DatabaseName string `gorm:"size:100;not null" json:"database_name"`
QueryContent string `gorm:"type:text;not null" json:"query_content"`
QueryType string `gorm:"size:50;not null" json:"query_type"`
ExecutionTimeMs int `gorm:"not null" json:"execution_time_ms"`
ResultRows int `gorm:"not null" json:"result_rows"`
IsSuccess bool `gorm:"not null" json:"is_success"`
ErrorMessage *string `gorm:"type:text" json:"error_message"`
IsFavorite bool `gorm:"not null" json:"is_favorite"`
Note string `gorm:"type:text;not null" json:"note"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (QueryHistory) TableName() string { return "query_histories" }
// ==================== Workflow ====================
type WorkflowDefinition struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Description *string `gorm:"type:text" json:"description"`
Category *string `gorm:"size:50" json:"category"`
IsDefault *bool `json:"is_default"`
IsActive *bool `json:"is_active"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
CreatedBy *int `json:"created_by"`
FlowConfig *string `gorm:"type:json" json:"flow_config"`
TriggerConditions *string `gorm:"type:json" json:"trigger_conditions"`
}
func (WorkflowDefinition) TableName() string { return "workflow_definitions" }
type WorkflowStep struct {
ID int `gorm:"primaryKey" json:"id"`
WorkflowID int `gorm:"not null" json:"workflow_id"`
StepNumber int `gorm:"not null" json:"step_number"`
Name string `gorm:"size:100;not null" json:"name"`
Description *string `gorm:"type:text" json:"description"`
StepType string `gorm:"size:20;not null" json:"step_type"`
ApproverRole *string `gorm:"size:20" json:"approver_role"`
ApproverIDs *string `gorm:"type:json" json:"approver_ids"`
ApproverAssignment *string `gorm:"type:json" json:"approver_assignment"`
IsRequired *bool `json:"is_required"`
TimeoutHours *int `json:"timeout_hours"`
AutoApproveOnTimeout *bool `json:"auto_approve_on_timeout"`
EntryConditions *string `gorm:"type:json" json:"entry_conditions"`
TransitionConditions *string `gorm:"type:json" json:"transition_conditions"`
FieldPermissions *string `gorm:"type:json" json:"field_permissions"`
}
func (WorkflowStep) TableName() string { return "workflow_steps" }
type WorkflowVersion struct {
ID int `gorm:"primaryKey" json:"id"`
WorkflowID int `gorm:"not null" json:"workflow_id"`
VersionNumber int `gorm:"not null" json:"version_number"`
VersionName *string `gorm:"size:100" json:"version_name"`
IsActive *bool `json:"is_active"`
StepsSnapshot *string `gorm:"type:json" json:"steps_snapshot"`
FlowConfigSnapshot *string `gorm:"type:json" json:"flow_config_snapshot"`
ChangeSummary *string `gorm:"type:text" json:"change_summary"`
CreatedBy *int `json:"created_by"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (WorkflowVersion) TableName() string { return "workflow_versions" }
type WorkflowExecutionLog struct {
ID int `gorm:"primaryKey" json:"id"`
WorkflowID int `gorm:"not null" json:"workflow_id"`
TicketID *int `json:"ticket_id"`
StepID *int `json:"step_id"`
ApprovalID *int `json:"approval_id"`
EventType string `gorm:"size:50;not null" json:"event_type"`
EventDescription *string `gorm:"type:text" json:"event_description"`
ActorID *int `json:"actor_id"`
ActorName *string `gorm:"size:100" json:"actor_name"`
ContextData *string `gorm:"type:json" json:"context_data"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (WorkflowExecutionLog) TableName() string { return "workflow_execution_logs" }
// ==================== Announcement ====================
type Announcement struct {
ID int `gorm:"primaryKey" json:"id"`
Title string `gorm:"size:200;not null" json:"title"`
Content string `gorm:"type:text;not null" json:"content"`
IsPublished *bool `json:"is_published"`
PublishedAt *time.Time `json:"published_at"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (Announcement) TableName() string { return "announcements" }
// ==================== InstanceGroup ====================
type InstanceGroup struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Description *string `gorm:"type:text" json:"description"`
DBType string `gorm:"size:20;not null" json:"db_type"`
Permissions *string `gorm:"type:text" json:"permissions"`
IsActive *bool `json:"is_active"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
WorkflowID *int `json:"workflow_id"`
AccessType *string `gorm:"size:20;default:readonly" json:"access_type"`
AllowedDatabases *string `gorm:"type:text" json:"allowed_databases"`
}
func (InstanceGroup) TableName() string { return "instance_groups" }
type InstanceGroupMember struct {
ID int `gorm:"primaryKey" json:"id"`
GroupID int `gorm:"not null" json:"group_id"`
InstanceID int `gorm:"not null" json:"instance_id"`
AccessType string `gorm:"default:readonly" json:"access_type"`
}
func (InstanceGroupMember) TableName() string { return "instance_group_members" }
// ==================== LDAP Config ====================
type LDAPConfig struct {
ID int `gorm:"primaryKey" json:"id"`
LDAPHost string `gorm:"size:200;not null" json:"ldap_host"`
LDAPPort *int `json:"ldap_port"`
UseLDAPS *bool `json:"use_ldaps"`
AdminDN string `gorm:"size:500;not null" json:"admin_dn"`
AdminPassword string `gorm:"size:500;not null" json:"admin_password"`
SearchFilter *string `gorm:"size:500" json:"search_filter"`
BaseDN string `gorm:"size:500;not null" json:"base_dn"`
UserMapping *string `gorm:"type:text" json:"user_mapping"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (LDAPConfig) TableName() string { return "ldap_config" }
// ==================== Notification ====================
type NotificationChannel struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
ChannelType string `gorm:"size:30;not null" json:"channel_type"`
Config string `gorm:"type:text;not null" json:"config"`
IsEnabled *bool `json:"is_enabled"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (NotificationChannel) TableName() string { return "notification_channels" }
type NotificationTemplate struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
EventType string `gorm:"size:50;not null" json:"event_type"`
ChannelType string `gorm:"size:30;not null" json:"channel_type"`
TitleTemplate *string `gorm:"type:text" json:"title_template"`
BodyTemplate *string `gorm:"type:text" json:"body_template"`
IsEnabled *bool `json:"is_enabled"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (NotificationTemplate) TableName() string { return "notification_templates" }
// ==================== Other Models ====================
type SystemSetting struct {
ID int `gorm:"primaryKey" json:"id"`
Key string `gorm:"size:100;not null" json:"key"`
Value string `gorm:"type:text;not null" json:"value"`
ValueType string `gorm:"size:20;not null" json:"value_type"`
Group string `gorm:"size:50;not null" json:"group"`
Label string `gorm:"size:100;not null" json:"label"`
Desc *string `gorm:"type:text" json:"description"`
DefaultValue *string `gorm:"type:text" json:"default_value"`
SortOrder int `gorm:"not null" json:"sort_order"`
UpdatedAt *time.Time `json:"updated_at"`
}
func (SystemSetting) TableName() string { return "system_settings" }
type OperationLog struct {
ID int `gorm:"primaryKey" json:"id"`
UserID *int `json:"user_id"`
Username *string `gorm:"size:50" json:"username"`
Action string `gorm:"size:50;not null" json:"action"`
ResourceType *string `gorm:"size:50" json:"resource_type"`
ResourceID *int `json:"resource_id"`
Detail *string `gorm:"type:text" json:"detail"`
IPAddress *string `gorm:"size:50" json:"ip_address"`
UserAgent *string `gorm:"size:255" json:"user_agent"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (OperationLog) TableName() string { return "operation_logs" }
type MaskingRule struct {
ID int `gorm:"primaryKey" json:"id"`
InstanceID int `gorm:"not null" json:"instance_id"`
Database string `gorm:"size:100;not null" json:"database"`
TblName string `gorm:"size:100;not null;column:table_name" json:"table_name"`
ColumnName string `gorm:"size:100;not null" json:"column_name"`
MaskType string `gorm:"size:30;not null" json:"mask_type"`
MaskConfig *string `gorm:"type:text" json:"mask_config"`
IsEnabled *bool `json:"is_enabled"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (MaskingRule) TableName() string { return "masking_rules" }
type Backup struct {
ID int `gorm:"primaryKey" json:"id"`
InstanceID int `gorm:"not null" json:"instance_id"`
Title string `gorm:"size:200;not null" json:"title"`
Database string `gorm:"size:100;not null" json:"database"`
Filename string `gorm:"size:500;not null" json:"filename"`
SizeBytes *int64 `json:"size_bytes"`
Status *string `gorm:"size:20" json:"status"`
CreatedBy *int `json:"created_by"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
CompletedAt *time.Time `json:"completed_at"`
Remark *string `gorm:"type:text" json:"remark"`
}
func (Backup) TableName() string { return "backups" }
type SchemaRevision struct {
ID int `gorm:"primaryKey" json:"id"`
InstanceID int `gorm:"not null" json:"instance_id"`
Database string `gorm:"size:100;not null" json:"database"`
Revision string `gorm:"size:64;not null" json:"revision"`
DDLContent *string `gorm:"type:text" json:"ddl_content"`
Checksum string `gorm:"size:64;not null" json:"checksum"`
AppliedBy *int `json:"applied_by"`
AppliedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"applied_at"`
Status *string `gorm:"size:20" json:"status"`
}
func (SchemaRevision) TableName() string { return "schema_revisions" }
type SlowLog struct {
ID int `gorm:"primaryKey" json:"id"`
InstanceID int `gorm:"not null" json:"instance_id"`
Database *string `gorm:"size:100" json:"database"`
SQLDigest *string `gorm:"size:64" json:"sql_digest"`
SQLText *string `gorm:"type:text" json:"sql_text"`
ExecutionCount *int `json:"execution_count"`
AvgTimeMs *int `json:"avg_time_ms"`
MaxTimeMs *int `json:"max_time_ms"`
RowsExamined *int `json:"rows_examined"`
RowsSent *int `json:"rows_sent"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (SlowLog) TableName() string { return "slow_logs" }
type ScheduledTask struct {
ID int `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Description *string `gorm:"type:text" json:"description"`
TaskSQLType string `gorm:"size:20;not null" json:"task_sql_type"`
InstanceID int `gorm:"not null" json:"instance_id"`
TargetDB *string `gorm:"size:100" json:"target_db"`
TargetTable *string `gorm:"size:100" json:"target_table"`
SQLContent string `gorm:"type:text;not null" json:"sql_content"`
CronExpression string `gorm:"size:50;not null" json:"cron_expression"`
IsEnabled *bool `json:"is_enabled"`
LastRunAt *time.Time `json:"last_run_at"`
LastRunStatus *string `gorm:"size:20" json:"last_run_status"`
LastRunMessage *string `gorm:"type:text" json:"last_run_message"`
NextRunAt *time.Time `json:"next_run_at"`
RunCount *int `json:"run_count"`
CreatedBy *int `json:"created_by"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (ScheduledTask) TableName() string { return "scheduled_tasks" }
type ScheduledTaskLog struct {
ID int `gorm:"primaryKey" json:"id"`
TaskID int `gorm:"not null" json:"task_id"`
Status string `gorm:"size:20;not null" json:"status"`
SQLContent *string `gorm:"type:text" json:"sql_content"`
AffectedRows *int `json:"affected_rows"`
DurationMs *int `json:"duration_ms"`
ErrorMessage *string `gorm:"type:text" json:"error_message"`
StartedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
}
func (ScheduledTaskLog) TableName() string { return "scheduled_task_logs" }
type UserGroup struct {
UserID int `gorm:"primaryKey" json:"user_id"`
GroupID int `gorm:"primaryKey" json:"group_id"`
Permissions *string `gorm:"type:text" json:"permissions"`
}
func (UserGroup) TableName() string { return "user_groups" }
// ==================== RefreshToken ====================
// RefreshToken 持久化 refresh token 的 SHA-256 摘要(明文不落库),支持过期与撤销。
type RefreshToken struct {
ID int `gorm:"primaryKey" json:"id"`
UserID int `gorm:"index;not null" json:"user_id"`
TokenHash string `gorm:"size:64;uniqueIndex;not null" json:"-"`
ExpiresAt time.Time `json:"expires_at"`
RevokedAt *time.Time `json:"revoked_at"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
}
func (RefreshToken) TableName() string { return "refresh_tokens" }
// ==================== DTO ====================
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
type LoginResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"`
TokenType string `json:"token_type"`
User User `json:"user"`
}
type TicketStatistics struct {
Total int `json:"total"`
Pending int `json:"pending"`
Reviewing int `json:"reviewing"`
Executing int `json:"executing"`
Completed int `json:"success"`
Failed int `json:"failed"`
Rejected int `json:"rejected"`
}
// DB is the global database instance
var DB *gorm.DB