Files
oneterm/backend/internal/service/history.go

129 lines
3.5 KiB
Go

package service
import (
"context"
"encoding/json"
"time"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"gorm.io/gorm"
myi18n "github.com/veops/oneterm/internal/i18n"
"github.com/veops/oneterm/internal/model"
"github.com/veops/oneterm/internal/repository"
dbpkg "github.com/veops/oneterm/pkg/db"
)
// HistoryService handles history business logic
type HistoryService struct {
repo repository.HistoryRepository
}
// NewHistoryService creates a new history service
func NewHistoryService() *HistoryService {
return &HistoryService{
repo: repository.NewHistoryRepository(),
}
}
// CreateHistory creates a new history record
func (s *HistoryService) CreateHistory(ctx context.Context, history *model.History) error {
return s.repo.CreateHistory(ctx, history)
}
// CreateHistoryRecord creates a history record with common fields
func (s *HistoryService) CreateHistoryRecord(ctx context.Context, actionType int, modelObj model.Model, oldModel model.Model, userId int) *model.History {
var clientIP string
if ginCtx, ok := ctx.(*gin.Context); ok {
clientIP = ginCtx.ClientIP()
}
return &model.History{
RemoteIp: clientIP,
Type: modelObj.TableName(),
TargetId: modelObj.GetId(),
ActionType: actionType,
Old: toMap(oldModel),
New: toMap(modelObj),
CreatorId: userId,
CreatedAt: time.Now(),
}
}
// CreateAndSaveHistory creates and saves a history record in one operation
func (s *HistoryService) CreateAndSaveHistory(ctx context.Context, actionType int, modelObj model.Model, oldModel model.Model, userId int) error {
history := s.CreateHistoryRecord(ctx, actionType, modelObj, oldModel, userId)
return s.CreateHistory(ctx, history)
}
// BuildQuery constructs history query with basic filters
func (s *HistoryService) BuildQuery(ctx *gin.Context) (*gorm.DB, error) {
db := dbpkg.DB.Model(&model.History{})
// Apply search filter
db = dbpkg.FilterSearch(ctx, db, "old", "new")
// Apply date range filter
db, err := s.filterStartEnd(ctx, db)
if err != nil {
return nil, err
}
// Apply exact match filters
db = dbpkg.FilterEqual(ctx, db, "type", "target_id", "action_type", "uid")
return db, nil
}
// GetTypeMapping gets mapping between history types and localized strings
func (s *HistoryService) GetTypeMapping(ctx *gin.Context) (map[string]string, error) {
lang := ctx.PostForm("lang")
accept := ctx.GetHeader("Accept-Language")
localizer := i18n.NewLocalizer(myi18n.Bundle, lang, accept)
cfg := &i18n.LocalizeConfig{}
key2msg := map[string]*i18n.Message{
"account": myi18n.MsgTypeMappingAccount,
"asset": myi18n.MsgTypeMappingAsset,
"command": myi18n.MsgTypeMappingCommand,
"gateway": myi18n.MsgTypeMappingGateway,
"node": myi18n.MsgTypeMappingNode,
"public_key": myi18n.MsgTypeMappingPublicKey,
}
data := make(map[string]string)
for k, v := range key2msg {
cfg.DefaultMessage = v
msg, err := localizer.Localize(cfg)
if err != nil {
return nil, err
}
data[k] = msg
}
return data, nil
}
// Helper methods for filtering
func (s *HistoryService) filterStartEnd(ctx *gin.Context, db *gorm.DB) (*gorm.DB, error) {
if start, ok := ctx.GetQuery("start"); ok {
db = db.Where("created_at >= ?", start)
}
if end, ok := ctx.GetQuery("end"); ok {
db = db.Where("created_at <= ?", end)
}
return db, nil
}
// toMap converts an object to a map
func toMap(data any) model.Map[string, any] {
if data == nil {
return nil
}
bs, _ := json.Marshal(data)
res := make(map[string]any)
json.Unmarshal(bs, &res)
return res
}