管理端按用户查询开启通讯录/相册/媒体拉取;服务端写入路径自动压图;新增 /v1/sign 与 /v1/energy。 Co-authored-by: Cursor <cursoragent@cursor.com>
73 lines
2.0 KiB
Go
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
|
|
}
|