spark-server/internal/module/energy/logic/checkin.go
DESKTOP-L8OUL9F\gfl0034 753c276496 feat: PopAdmin 拉取管理、签到能量 API 与图片 150KiB 硬上限
管理端按用户查询开启通讯录/相册/媒体拉取;服务端写入路径自动压图;新增 /v1/sign 与 /v1/energy。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-02 12:44:06 +08:00

73 lines
2.0 KiB
Go

package logic
import (
"context"
"errors"
"time"
"spark-server/internal/module/energy/entity"
"spark-server/internal/pkg/errs"
)
// CheckInResult is returned after a successful daily sign-in.
type CheckInResult struct {
Day int `json:"day"`
RewardPoint int `json:"reward_point"`
ContinueDays int `json:"continue_days"`
NeedDays int `json:"need_days"`
Energy int64 `json:"energy"`
Kind int16 `json:"kind"`
}
// CheckIn signs the current local day and credits energy.
func (s *Service) CheckIn(ctx context.Context, userID int64) (*CheckInResult, error) {
if !s.ready() || userID <= 0 {
return nil, errs.ErrUnauthorized
}
today := dayKeyOf(s.now())
return s.signDay(ctx, userID, today, entity.KindSign, "签到奖励")
}
func (s *Service) signDay(ctx context.Context, userID int64, day int, kind int16, source string) (*CheckInResult, error) {
if _, ok := parseDayKey(day); !ok {
return nil, errs.ErrInvalidArgument
}
if _, err := s.repo.GetSign(ctx, userID, day); err == nil {
return nil, errs.ErrDuplicate
} else if err != nil && !errors.Is(err, errs.ErrNotFound) {
return nil, err
}
streak, _ := s.computeStreak(ctx, userID, dayKeyOf(s.now()))
rewardDay := streak + 1
if kind == entity.KindMakeup {
rewardDay = 1
}
reward := entity.CycleRewards(rewardDay)
reason := "sign"
if kind == entity.KindMakeup {
reason = "makeup"
}
rec := &entity.SignRecord{
UserID: userID, DayKey: day, Kind: kind, Reward: reward,
CreatedMS: time.Now().UnixMilli(),
}
if err := s.repo.CreateSign(ctx, rec); err != nil {
return nil, err
}
wallet, _, err := s.repo.AddEnergy(ctx, userID, int64(reward), reason, day, source)
if err != nil {
return nil, err
}
newStreak, _ := s.computeStreak(ctx, userID, dayKeyOf(s.now()))
need := entity.DefaultNeedDays - (newStreak % entity.DefaultNeedDays)
if need <= 0 {
need = entity.DefaultNeedDays
}
return &CheckInResult{
Day: day, RewardPoint: reward, ContinueDays: newStreak,
NeedDays: need, Energy: wallet.Energy, Kind: kind,
}, nil
}