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

260 lines
10 KiB
Go

package grpc
import (
"context"
"time"
activityv1 "hyapp.local/api/proto/activity/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
domain "hyapp/services/activity-service/internal/domain/achievement"
service "hyapp/services/activity-service/internal/service/achievement"
)
// AchievementServer adapts achievement and badge display use cases to gRPC.
type AchievementServer struct {
activityv1.UnimplementedAchievementServiceServer
svc *service.Service
}
func NewAchievementServer(svc *service.Service) *AchievementServer {
return &AchievementServer{svc: svc}
}
func (s *AchievementServer) ListAchievements(ctx context.Context, req *activityv1.ListAchievementsRequest) (*activityv1.ListAchievementsResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
items, total, err := s.svc.ListAchievements(ctx, domain.ListQuery{
UserID: req.GetUserId(),
CollectionType: req.GetCollectionType(),
CollectionID: req.GetCollectionId(),
Page: req.GetPage(),
PageSize: req.GetPageSize(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
resp := &activityv1.ListAchievementsResponse{Achievements: make([]*activityv1.UserAchievement, 0, len(items)), Total: total, ServerTimeMs: time.Now().UnixMilli()}
for _, item := range items {
resp.Achievements = append(resp.Achievements, userAchievementToProto(item))
}
return resp, nil
}
func (s *AchievementServer) ConsumeAchievementEvent(ctx context.Context, req *activityv1.ConsumeAchievementEventRequest) (*activityv1.ConsumeAchievementEventResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
result, err := s.svc.ConsumeAchievementEvent(ctx, domain.Event{
EventID: req.GetEventId(),
EventType: req.GetEventType(),
SourceService: req.GetSourceService(),
UserID: req.GetUserId(),
MetricType: req.GetMetricType(),
Value: req.GetValue(),
OccurredAtMS: req.GetOccurredAtMs(),
DimensionsJSON: req.GetDimensionsJson(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &activityv1.ConsumeAchievementEventResponse{
EventId: result.EventID,
Status: result.Status,
MatchedAchievementCount: result.MatchedAchievementCount,
UnlockedAchievementCount: result.UnlockedAchievementCount,
RewardJobCount: result.RewardJobCount,
}, nil
}
func (s *AchievementServer) ListMyBadges(ctx context.Context, req *activityv1.ListMyBadgesRequest) (*activityv1.ListMyBadgesResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
profile, err := s.svc.ListBadgeProfile(ctx, req.GetUserId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return badgeProfileToProto(profile), nil
}
func (s *AchievementServer) SetBadgeDisplay(ctx context.Context, req *activityv1.SetBadgeDisplayRequest) (*activityv1.SetBadgeDisplayResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
items := make([]domain.BadgeDisplayItem, 0, len(req.GetItems()))
for _, item := range req.GetItems() {
items = append(items, badgeDisplayItemFromProto(item))
}
profile, err := s.svc.SetBadgeDisplay(ctx, domain.DisplayCommand{
UserID: req.GetUserId(),
Slot: req.GetSlot(),
Items: items,
CommandID: req.GetCommandId(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &activityv1.SetBadgeDisplayResponse{Profile: badgeProfileToProto(profile)}, nil
}
// AdminAchievementServer adapts admin achievement configuration commands to gRPC.
type AdminAchievementServer struct {
activityv1.UnimplementedAdminAchievementServiceServer
svc *service.Service
}
func NewAdminAchievementServer(svc *service.Service) *AdminAchievementServer {
return &AdminAchievementServer{svc: svc}
}
func (s *AdminAchievementServer) ListAchievementDefinitions(ctx context.Context, req *activityv1.ListAchievementsRequest) (*activityv1.ListAchievementsResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
items, total, err := s.svc.ListAchievementDefinitions(ctx, domain.ListQuery{
CollectionType: req.GetCollectionType(),
CollectionID: req.GetCollectionId(),
Page: req.GetPage(),
PageSize: req.GetPageSize(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
resp := &activityv1.ListAchievementsResponse{Achievements: make([]*activityv1.UserAchievement, 0, len(items)), Total: total, ServerTimeMs: time.Now().UnixMilli()}
for _, item := range items {
resp.Achievements = append(resp.Achievements, &activityv1.UserAchievement{Definition: achievementDefinitionToProto(item)})
}
return resp, nil
}
func (s *AdminAchievementServer) UpsertAchievementDefinition(ctx context.Context, req *activityv1.UpsertAchievementDefinitionRequest) (*activityv1.UpsertAchievementDefinitionResponse, error) {
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
conditions := make([]domain.ConditionCommand, 0, len(req.GetConditions()))
for _, condition := range req.GetConditions() {
conditions = append(conditions, domain.ConditionCommand{
MetricType: condition.GetMetricType(),
TargetValue: condition.GetTargetValue(),
TargetUnit: condition.GetTargetUnit(),
DimensionFilterJSON: condition.GetDimensionFilterJson(),
})
}
item, created, err := s.svc.UpsertAchievementDefinition(ctx, domain.DefinitionCommand{
AchievementID: req.GetAchievementId(),
CollectionID: req.GetCollectionId(),
CollectionType: req.GetCollectionType(),
AchievementType: req.GetAchievementType(),
Title: req.GetTitle(),
Description: req.GetDescription(),
Status: req.GetStatus(),
PrimaryBadgeResourceID: req.GetPrimaryBadgeResourceId(),
RewardResourceGroupID: req.GetRewardResourceGroupId(),
AutoPinPolicy: req.GetAutoPinPolicy(),
HonorVisibility: req.GetHonorVisibility(),
EffectiveFromMS: req.GetEffectiveFromMs(),
EffectiveToMS: req.GetEffectiveToMs(),
SortOrder: req.GetSortOrder(),
DisplayConfigJSON: req.GetDisplayConfigJson(),
OperatorAdminID: req.GetOperatorAdminId(),
Conditions: conditions,
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &activityv1.UpsertAchievementDefinitionResponse{Achievement: achievementDefinitionToProto(item), Created: created}, nil
}
func userAchievementToProto(item domain.UserAchievement) *activityv1.UserAchievement {
return &activityv1.UserAchievement{
Definition: achievementDefinitionToProto(item.Definition),
CycleKey: item.CycleKey,
ProgressValue: item.ProgressValue,
TargetValue: item.TargetValue,
UserStatus: item.UserStatus,
UnlockedAtMs: item.UnlockedAtMS,
RewardStatus: item.RewardStatus,
}
}
func achievementDefinitionToProto(item domain.Definition) *activityv1.AchievementDefinition {
resp := &activityv1.AchievementDefinition{
AchievementId: item.AchievementID,
CollectionId: item.CollectionID,
CollectionType: item.CollectionType,
AchievementType: item.AchievementType,
Title: item.Title,
Description: item.Description,
Status: item.Status,
Version: item.Version,
PrimaryBadgeResourceId: item.PrimaryBadgeResourceID,
RewardResourceGroupId: item.RewardResourceGroupID,
AutoPinPolicy: item.AutoPinPolicy,
HonorVisibility: item.HonorVisibility,
EffectiveFromMs: item.EffectiveFromMS,
EffectiveToMs: item.EffectiveToMS,
SortOrder: item.SortOrder,
DisplayConfigJson: item.DisplayConfigJSON,
CreatedByAdminId: item.CreatedByAdminID,
UpdatedByAdminId: item.UpdatedByAdminID,
CreatedAtMs: item.CreatedAtMS,
UpdatedAtMs: item.UpdatedAtMS,
Conditions: make([]*activityv1.AchievementCondition, 0, len(item.Conditions)),
}
for _, condition := range item.Conditions {
resp.Conditions = append(resp.Conditions, achievementConditionToProto(condition))
}
return resp
}
func achievementConditionToProto(item domain.Condition) *activityv1.AchievementCondition {
return &activityv1.AchievementCondition{
ConditionId: item.ConditionID,
AchievementId: item.AchievementID,
Version: item.Version,
MetricType: item.MetricType,
TargetValue: item.TargetValue,
TargetUnit: item.TargetUnit,
DimensionFilterJson: item.DimensionFilterJSON,
}
}
func badgeProfileToProto(profile domain.BadgeProfile) *activityv1.ListMyBadgesResponse {
resp := &activityv1.ListMyBadgesResponse{
StripBadges: make([]*activityv1.BadgeDisplayItem, 0, len(profile.StripBadges)),
ProfileTileBadges: make([]*activityv1.BadgeDisplayItem, 0, len(profile.ProfileTileBadges)),
HonorBadges: make([]*activityv1.BadgeDisplayItem, 0, len(profile.HonorBadges)),
ServerTimeMs: profile.ServerTimeMS,
}
for _, item := range profile.StripBadges {
resp.StripBadges = append(resp.StripBadges, badgeDisplayItemToProto(item))
}
for _, item := range profile.ProfileTileBadges {
resp.ProfileTileBadges = append(resp.ProfileTileBadges, badgeDisplayItemToProto(item))
}
for _, item := range profile.HonorBadges {
resp.HonorBadges = append(resp.HonorBadges, badgeDisplayItemToProto(item))
}
return resp
}
func badgeDisplayItemToProto(item domain.BadgeDisplayItem) *activityv1.BadgeDisplayItem {
return &activityv1.BadgeDisplayItem{
Slot: item.Slot,
Position: item.Position,
BadgeForm: item.BadgeForm,
ResourceId: item.ResourceID,
EntitlementId: item.EntitlementID,
SourceType: item.SourceType,
SourceId: item.SourceID,
PinMode: item.PinMode,
UpdatedAtMs: item.UpdatedAtMS,
}
}
func badgeDisplayItemFromProto(item *activityv1.BadgeDisplayItem) domain.BadgeDisplayItem {
return domain.BadgeDisplayItem{
Slot: item.GetSlot(),
Position: item.GetPosition(),
BadgeForm: item.GetBadgeForm(),
ResourceID: item.GetResourceId(),
EntitlementID: item.GetEntitlementId(),
SourceType: item.GetSourceType(),
SourceID: item.GetSourceId(),
PinMode: item.GetPinMode(),
UpdatedAtMS: item.GetUpdatedAtMs(),
}
}