2026-05-14 17:44:16 +08:00

103 lines
3.7 KiB
Go

package grpc
import (
"context"
"time"
activityv1 "hyapp.local/api/proto/activity/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
achievementservice "hyapp/services/activity-service/internal/service/achievement"
growthservice "hyapp/services/activity-service/internal/service/growth"
messageservice "hyapp/services/activity-service/internal/service/message"
)
// CronServer exposes activity-service owned background batches to cron-service.
type CronServer struct {
activityv1.UnimplementedActivityCronServiceServer
message *messageservice.Service
growth *growthservice.Service
achievement *achievementservice.Service
}
// NewCronServer creates the internal cron adapter without exposing message storage.
func NewCronServer(messageSvc *messageservice.Service, growthSvc *growthservice.Service, achievementSvc ...*achievementservice.Service) *CronServer {
server := &CronServer{message: messageSvc}
server.growth = growthSvc
if len(achievementSvc) > 0 {
server.achievement = achievementSvc[0]
}
return server
}
// ProcessMessageFanoutBatch claims and processes one message fanout page.
func (s *CronServer) ProcessMessageFanoutBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
if s.message == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "message service is not configured"))
}
processed, err := s.message.ProcessNextFanoutJob(ctx, messageservice.FanoutWorkerOptions{
WorkerID: req.GetWorkerId(),
BatchSize: int(req.GetBatchSize()),
LockTTL: durationFromMillis(req.GetLockTtlMs()),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
if !processed {
return &activityv1.CronBatchResponse{}, nil
}
return &activityv1.CronBatchResponse{
ClaimedCount: 1,
ProcessedCount: 1,
SuccessCount: 1,
HasMore: true,
}, nil
}
// ProcessLevelRewardBatch claims and grants pending permanent level rewards.
func (s *CronServer) ProcessLevelRewardBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
if s.growth == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "growth service is not configured"))
}
claimed, processed, success, failure, hasMore, err := s.growth.ProcessLevelRewardBatch(ctx, req.GetRunId(), req.GetWorkerId(), int(req.GetBatchSize()), durationFromMillis(req.GetLockTtlMs()))
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &activityv1.CronBatchResponse{
ClaimedCount: claimed,
ProcessedCount: processed,
SuccessCount: success,
FailureCount: failure,
HasMore: hasMore,
}, nil
}
// ProcessAchievementRewardBatch claims and grants pending achievement badge/resource rewards.
func (s *CronServer) ProcessAchievementRewardBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
if s.achievement == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "achievement service is not configured"))
}
claimed, processed, success, failure, hasMore, err := s.achievement.ProcessAchievementRewardBatch(ctx, req.GetRunId(), req.GetWorkerId(), int(req.GetBatchSize()), durationFromMillis(req.GetLockTtlMs()))
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &activityv1.CronBatchResponse{
ClaimedCount: claimed,
ProcessedCount: processed,
SuccessCount: success,
FailureCount: failure,
HasMore: hasMore,
}, nil
}
func durationFromMillis(value int64) time.Duration {
if value <= 0 {
return 0
}
return time.Duration(value) * time.Millisecond
}