package logic import ( "context" "spark-server/internal/module/energy/entity" "spark-server/internal/pkg/errs" ) // LogItem is one energy ledger row for the client bill page. type LogItem struct { Source string `json:"source"` Date int64 `json:"date"` Value int64 `json:"value"` ID int64 `json:"id"` } // LogsView is a cursor page of energy details. type LogsView struct { Items []LogItem `json:"reward_details"` NextCursor int64 `json:"next_cursor"` IsEnd bool `json:"is_end"` Energy int64 `json:"energy"` } // Logs lists energy changes newest-first. cursor is the last seen log id (0 = start). func (s *Service) Logs(ctx context.Context, userID, cursor int64, limit int) (*LogsView, error) { if !s.ready() || userID <= 0 { return nil, errs.ErrUnauthorized } limit = entity.ClampLimit(limit) rows, err := s.repo.ListEnergyLogs(ctx, userID, cursor, limit+1) if err != nil { return nil, err } end := true if len(rows) > limit { end = false rows = rows[:limit] } items := make([]LogItem, 0, len(rows)) var next int64 for _, r := range rows { src := r.Source if src == "" { src = r.Reason } items = append(items, LogItem{Source: src, Date: r.CreatedMS / 1000, Value: r.Delta, ID: r.ID}) next = r.ID } bal, err := s.repo.EnsureWallet(ctx, userID) if err != nil { return nil, err } return &LogsView{Items: items, NextCursor: next, IsEnd: end, Energy: bal.Energy}, nil }