vip fixed
This commit is contained in:
parent
bf07c84dd0
commit
f8a8d4d65f
@ -105,9 +105,17 @@ func (s *Service) PageUserStates(
|
|||||||
Find(&rows).Error; err != nil {
|
Find(&rows).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
resourceIDs := make([]int64, 0, len(rows)*5)
|
||||||
|
for idx := range rows {
|
||||||
|
resourceIDs = appendResourceIDsFromState(resourceIDs, &rows[idx])
|
||||||
|
}
|
||||||
|
resources, err := s.loadResourceLookup(ctx, resourceIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
records = make([]UserVipStatePayload, 0, len(rows))
|
records = make([]UserVipStatePayload, 0, len(rows))
|
||||||
for idx := range rows {
|
for idx := range rows {
|
||||||
records = append(records, statePayloadFromModel(&rows[idx], now))
|
records = append(records, statePayloadFromModelWithResources(&rows[idx], now, resources))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,13 +36,19 @@ func (s *Service) GetHome(ctx context.Context, user AuthUser) (*HomeResponse, er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
activeState := activeStateOrNil(state, now)
|
activeState := activeStateOrNil(state, now)
|
||||||
|
resourceIDs := resourceIDsFromConfigMap(configs)
|
||||||
|
resourceIDs = appendResourceIDsFromState(resourceIDs, state)
|
||||||
|
resources, err := s.loadResourceLookup(ctx, resourceIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
levels := make([]VipLevelHomePayload, 0, levelCount)
|
levels := make([]VipLevelHomePayload, 0, levelCount)
|
||||||
for level := 1; level <= levelCount; level++ {
|
for level := 1; level <= levelCount; level++ {
|
||||||
payload := defaultLevelPayload(level)
|
payload := defaultLevelPayload(level)
|
||||||
config, exists := configs[level]
|
config, exists := configs[level]
|
||||||
if exists {
|
if exists {
|
||||||
payload = payloadFromConfig(config)
|
payload = payloadFromConfigWithResources(config, resources)
|
||||||
}
|
}
|
||||||
item := VipLevelHomePayload{VipLevelPayload: payload}
|
item := VipLevelHomePayload{VipLevelPayload: payload}
|
||||||
if exists && config.Enabled {
|
if exists && config.Enabled {
|
||||||
@ -55,7 +61,7 @@ func (s *Service) GetHome(ctx context.Context, user AuthUser) (*HomeResponse, er
|
|||||||
levels = append(levels, item)
|
levels = append(levels, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
statePayload := statePayloadFromModel(state, now)
|
statePayload := statePayloadFromModelWithResources(state, now, resources)
|
||||||
statePayload.UserID = user.UserID
|
statePayload.UserID = user.UserID
|
||||||
statePayload.SysOrigin = sysOrigin
|
statePayload.SysOrigin = sysOrigin
|
||||||
return &HomeResponse{
|
return &HomeResponse{
|
||||||
@ -78,7 +84,11 @@ func (s *Service) GetStatus(ctx context.Context, user AuthUser) (*UserVipStatePa
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload := statePayloadFromModel(state, now)
|
resources, err := s.loadResourceLookup(ctx, appendResourceIDsFromState(nil, state))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload := statePayloadFromModelWithResources(state, now, resources)
|
||||||
payload.UserID = user.UserID
|
payload.UserID = user.UserID
|
||||||
payload.SysOrigin = sysOrigin
|
payload.SysOrigin = sysOrigin
|
||||||
return &payload, nil
|
return &payload, nil
|
||||||
@ -112,6 +122,10 @@ func (s *Service) PreviewPurchase(ctx context.Context, user AuthUser, req Purcha
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
resources, err := s.loadResourceLookup(ctx, appendResourceIDsFromConfig(nil, plan.TargetConfig))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &PurchasePreviewResponse{
|
return &PurchasePreviewResponse{
|
||||||
UserID: user.UserID,
|
UserID: user.UserID,
|
||||||
@ -124,7 +138,7 @@ func (s *Service) PreviewPurchase(ctx context.Context, user AuthUser, req Purcha
|
|||||||
PayableGold: plan.PayableGold,
|
PayableGold: plan.PayableGold,
|
||||||
StartAt: formatTime(plan.StartAt),
|
StartAt: formatTime(plan.StartAt),
|
||||||
ExpireAt: formatTime(plan.ExpireAt),
|
ExpireAt: formatTime(plan.ExpireAt),
|
||||||
TargetConfig: payloadFromConfig(plan.TargetConfig),
|
TargetConfig: payloadFromConfigWithResources(plan.TargetConfig, resources),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +173,11 @@ func (s *Service) Purchase(ctx context.Context, user AuthUser, req PurchaseReque
|
|||||||
if err := s.ensureStateResourcesGranted(ctx, user.UserID, state, now); err != nil {
|
if err := s.ensureStateResourcesGranted(ctx, user.UserID, state, now); err != nil {
|
||||||
return nil, NewAppError(http.StatusBadGateway, "vip_resource_grant_failed", err.Error())
|
return nil, NewAppError(http.StatusBadGateway, "vip_resource_grant_failed", err.Error())
|
||||||
}
|
}
|
||||||
statePayload := statePayloadFromModel(state, now)
|
resources, resourceErr := s.loadResourceLookup(ctx, appendResourceIDsFromState(nil, state))
|
||||||
|
if resourceErr != nil {
|
||||||
|
return nil, resourceErr
|
||||||
|
}
|
||||||
|
statePayload := statePayloadFromModelWithResources(state, now, resources)
|
||||||
statePayload.UserID = user.UserID
|
statePayload.UserID = user.UserID
|
||||||
statePayload.SysOrigin = sysOrigin
|
statePayload.SysOrigin = sysOrigin
|
||||||
return &PurchaseResponse{
|
return &PurchaseResponse{
|
||||||
@ -275,7 +293,13 @@ func (s *Service) Purchase(ctx context.Context, user AuthUser, req PurchaseReque
|
|||||||
return nil, NewAppError(http.StatusBadGateway, "vip_resource_grant_failed", err.Error())
|
return nil, NewAppError(http.StatusBadGateway, "vip_resource_grant_failed", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := statePayloadFromModel(state, now)
|
resources, err := s.loadResourceLookup(ctx, appendResourceIDsFromState(nil, state))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload := statePayloadFromModelWithResources(state, now, resources)
|
||||||
|
payload.UserID = user.UserID
|
||||||
|
payload.SysOrigin = sysOrigin
|
||||||
return &PurchaseResponse{
|
return &PurchaseResponse{
|
||||||
Success: true,
|
Success: true,
|
||||||
Order: orderPayloadFromModel(order),
|
Order: orderPayloadFromModel(order),
|
||||||
@ -451,20 +475,20 @@ func applyPlanToState(state *model.UserVipState, plan purchasePlan, now time.Tim
|
|||||||
state.StartAt = plan.StartAt
|
state.StartAt = plan.StartAt
|
||||||
state.ExpireAt = plan.ExpireAt
|
state.ExpireAt = plan.ExpireAt
|
||||||
state.BadgeResourceID = plan.TargetConfig.BadgeResourceID
|
state.BadgeResourceID = plan.TargetConfig.BadgeResourceID
|
||||||
state.BadgeName = plan.TargetConfig.BadgeName
|
state.BadgeName = ""
|
||||||
state.BadgeURL = plan.TargetConfig.BadgeURL
|
state.BadgeURL = ""
|
||||||
state.AvatarFrameResourceID = plan.TargetConfig.AvatarFrameResourceID
|
state.AvatarFrameResourceID = plan.TargetConfig.AvatarFrameResourceID
|
||||||
state.AvatarFrameName = plan.TargetConfig.AvatarFrameName
|
state.AvatarFrameName = ""
|
||||||
state.AvatarFrameURL = plan.TargetConfig.AvatarFrameURL
|
state.AvatarFrameURL = ""
|
||||||
state.EntryEffectResourceID = plan.TargetConfig.EntryEffectResourceID
|
state.EntryEffectResourceID = plan.TargetConfig.EntryEffectResourceID
|
||||||
state.EntryEffectName = plan.TargetConfig.EntryEffectName
|
state.EntryEffectName = ""
|
||||||
state.EntryEffectURL = plan.TargetConfig.EntryEffectURL
|
state.EntryEffectURL = ""
|
||||||
state.ChatBubbleResourceID = plan.TargetConfig.ChatBubbleResourceID
|
state.ChatBubbleResourceID = plan.TargetConfig.ChatBubbleResourceID
|
||||||
state.ChatBubbleName = plan.TargetConfig.ChatBubbleName
|
state.ChatBubbleName = ""
|
||||||
state.ChatBubbleURL = plan.TargetConfig.ChatBubbleURL
|
state.ChatBubbleURL = ""
|
||||||
state.FloatPictureResourceID = plan.TargetConfig.FloatPictureResourceID
|
state.FloatPictureResourceID = plan.TargetConfig.FloatPictureResourceID
|
||||||
state.FloatPictureName = plan.TargetConfig.FloatPictureName
|
state.FloatPictureName = ""
|
||||||
state.FloatPictureURL = plan.TargetConfig.FloatPictureURL
|
state.FloatPictureURL = ""
|
||||||
state.UpdateTime = now
|
state.UpdateTime = now
|
||||||
if state.CreateTime.IsZero() {
|
if state.CreateTime.IsZero() {
|
||||||
state.CreateTime = now
|
state.CreateTime = now
|
||||||
|
|||||||
@ -25,6 +25,10 @@ func (s *Service) GetConfig(ctx context.Context, sysOrigin string) (*ConfigRespo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
resources, err := s.loadResourceLookup(ctx, resourceIDsFromConfigRows(rows))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
levels := buildDefaultLevelPayloads()
|
levels := buildDefaultLevelPayloads()
|
||||||
configured := len(rows) > 0
|
configured := len(rows) > 0
|
||||||
@ -33,7 +37,7 @@ func (s *Service) GetConfig(ctx context.Context, sysOrigin string) (*ConfigRespo
|
|||||||
if row.Level < 1 || row.Level > levelCount {
|
if row.Level < 1 || row.Level > levelCount {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
levels[row.Level-1] = payloadFromConfig(row)
|
levels[row.Level-1] = payloadFromConfigWithResources(row, resources)
|
||||||
if row.UpdateTime.After(updateTime) {
|
if row.UpdateTime.After(updateTime) {
|
||||||
updateTime = row.UpdateTime
|
updateTime = row.UpdateTime
|
||||||
}
|
}
|
||||||
@ -138,14 +142,20 @@ func normalizeResource(resource VipResourcePayload) VipResourcePayload {
|
|||||||
resource.URL = strings.TrimSpace(resource.URL)
|
resource.URL = strings.TrimSpace(resource.URL)
|
||||||
resource.SourceURL = strings.TrimSpace(resource.SourceURL)
|
resource.SourceURL = strings.TrimSpace(resource.SourceURL)
|
||||||
resource.ResourceURL = strings.TrimSpace(resource.ResourceURL)
|
resource.ResourceURL = strings.TrimSpace(resource.ResourceURL)
|
||||||
|
resource.Cover = strings.TrimSpace(resource.Cover)
|
||||||
|
resource.CoverURL = strings.TrimSpace(resource.CoverURL)
|
||||||
if resource.URL == "" {
|
if resource.URL == "" {
|
||||||
resource.URL = resource.SourceURL
|
resource.URL = resource.SourceURL
|
||||||
}
|
}
|
||||||
if resource.URL == "" {
|
if resource.URL == "" {
|
||||||
resource.URL = resource.ResourceURL
|
resource.URL = resource.ResourceURL
|
||||||
}
|
}
|
||||||
|
if resource.Cover == "" {
|
||||||
|
resource.Cover = resource.CoverURL
|
||||||
|
}
|
||||||
resource.SourceURL = resource.URL
|
resource.SourceURL = resource.URL
|
||||||
resource.ResourceURL = resource.URL
|
resource.ResourceURL = resource.URL
|
||||||
|
resource.CoverURL = resource.Cover
|
||||||
return resource
|
return resource
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,20 +167,20 @@ func applyLevelPayloadToConfig(row *model.VipLevelConfig, item VipLevelPayload,
|
|||||||
row.DurationDays = vipDurationDay
|
row.DurationDays = vipDurationDay
|
||||||
row.PriceGold = item.PriceGold
|
row.PriceGold = item.PriceGold
|
||||||
row.BadgeResourceID = item.Badge.ResourceID.Int64()
|
row.BadgeResourceID = item.Badge.ResourceID.Int64()
|
||||||
row.BadgeName = item.Badge.Name
|
row.BadgeName = ""
|
||||||
row.BadgeURL = item.Badge.URL
|
row.BadgeURL = ""
|
||||||
row.AvatarFrameResourceID = item.AvatarFrame.ResourceID.Int64()
|
row.AvatarFrameResourceID = item.AvatarFrame.ResourceID.Int64()
|
||||||
row.AvatarFrameName = item.AvatarFrame.Name
|
row.AvatarFrameName = ""
|
||||||
row.AvatarFrameURL = item.AvatarFrame.URL
|
row.AvatarFrameURL = ""
|
||||||
row.EntryEffectResourceID = item.EntryEffect.ResourceID.Int64()
|
row.EntryEffectResourceID = item.EntryEffect.ResourceID.Int64()
|
||||||
row.EntryEffectName = item.EntryEffect.Name
|
row.EntryEffectName = ""
|
||||||
row.EntryEffectURL = item.EntryEffect.URL
|
row.EntryEffectURL = ""
|
||||||
row.ChatBubbleResourceID = item.ChatBubble.ResourceID.Int64()
|
row.ChatBubbleResourceID = item.ChatBubble.ResourceID.Int64()
|
||||||
row.ChatBubbleName = item.ChatBubble.Name
|
row.ChatBubbleName = ""
|
||||||
row.ChatBubbleURL = item.ChatBubble.URL
|
row.ChatBubbleURL = ""
|
||||||
row.FloatPictureResourceID = item.FloatPicture.ResourceID.Int64()
|
row.FloatPictureResourceID = item.FloatPicture.ResourceID.Int64()
|
||||||
row.FloatPictureName = item.FloatPicture.Name
|
row.FloatPictureName = ""
|
||||||
row.FloatPictureURL = item.FloatPicture.URL
|
row.FloatPictureURL = ""
|
||||||
row.UpdateTime = now
|
row.UpdateTime = now
|
||||||
if row.CreateTime.IsZero() {
|
if row.CreateTime.IsZero() {
|
||||||
row.CreateTime = now
|
row.CreateTime = now
|
||||||
|
|||||||
@ -106,7 +106,7 @@ func TestBuildPurchasePlanRejectsDowngrade(t *testing.T) {
|
|||||||
|
|
||||||
func TestVipResourcePayloadAcceptsStringResourceID(t *testing.T) {
|
func TestVipResourcePayloadAcceptsStringResourceID(t *testing.T) {
|
||||||
var payload VipResourcePayload
|
var payload VipResourcePayload
|
||||||
if err := json.Unmarshal([]byte(`{"resourceId":"2045029471274201089","name":"vip","sourceUrl":"https://example.com/a.svga"}`), &payload); err != nil {
|
if err := json.Unmarshal([]byte(`{"resourceId":"2045029471274201089","name":"vip","sourceUrl":"https://example.com/a.svga","coverUrl":"https://example.com/a.png"}`), &payload); err != nil {
|
||||||
t.Fatalf("unmarshal resource payload: %v", err)
|
t.Fatalf("unmarshal resource payload: %v", err)
|
||||||
}
|
}
|
||||||
payload = normalizeResource(payload)
|
payload = normalizeResource(payload)
|
||||||
@ -116,16 +116,100 @@ func TestVipResourcePayloadAcceptsStringResourceID(t *testing.T) {
|
|||||||
if payload.URL != "https://example.com/a.svga" || payload.SourceURL != payload.URL || payload.ResourceURL != payload.URL {
|
if payload.URL != "https://example.com/a.svga" || payload.SourceURL != payload.URL || payload.ResourceURL != payload.URL {
|
||||||
t.Fatalf("resource urls = url:%q sourceUrl:%q resourceUrl:%q", payload.URL, payload.SourceURL, payload.ResourceURL)
|
t.Fatalf("resource urls = url:%q sourceUrl:%q resourceUrl:%q", payload.URL, payload.SourceURL, payload.ResourceURL)
|
||||||
}
|
}
|
||||||
|
if payload.Cover != "https://example.com/a.png" || payload.CoverURL != payload.Cover {
|
||||||
|
t.Fatalf("resource cover = cover:%q coverUrl:%q", payload.Cover, payload.CoverURL)
|
||||||
|
}
|
||||||
|
|
||||||
body, err := json.Marshal(payload)
|
body, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("marshal resource payload: %v", err)
|
t.Fatalf("marshal resource payload: %v", err)
|
||||||
}
|
}
|
||||||
if got := string(body); got != `{"resourceId":"2045029471274201089","name":"vip","url":"https://example.com/a.svga","sourceUrl":"https://example.com/a.svga","resourceUrl":"https://example.com/a.svga"}` {
|
if got := string(body); got != `{"resourceId":"2045029471274201089","name":"vip","url":"https://example.com/a.svga","sourceUrl":"https://example.com/a.svga","resourceUrl":"https://example.com/a.svga","cover":"https://example.com/a.png","coverUrl":"https://example.com/a.png"}` {
|
||||||
t.Fatalf("json = %s", got)
|
t.Fatalf("json = %s", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourcePayloadUsesResourceGroupDetails(t *testing.T) {
|
||||||
|
row := model.VipLevelConfig{
|
||||||
|
Level: 2,
|
||||||
|
LevelCode: "VIP2",
|
||||||
|
DisplayName: "VIP 2",
|
||||||
|
Enabled: true,
|
||||||
|
DurationDays: vipDurationDay,
|
||||||
|
PriceGold: 2000,
|
||||||
|
AvatarFrameResourceID: 11,
|
||||||
|
AvatarFrameName: "stale frame",
|
||||||
|
AvatarFrameURL: "https://example.com/stale.svga",
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := payloadFromConfigWithResources(row, vipResourceLookup{
|
||||||
|
11: {
|
||||||
|
ID: 11,
|
||||||
|
Name: "resource group frame",
|
||||||
|
Cover: "https://example.com/live.png",
|
||||||
|
SourceURL: "https://example.com/live.svga",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if payload.AvatarFrame.Name != "resource group frame" ||
|
||||||
|
payload.AvatarFrame.URL != "https://example.com/live.svga" ||
|
||||||
|
payload.AvatarFrame.Cover != "https://example.com/live.png" {
|
||||||
|
t.Fatalf("avatar frame was not loaded from resource group: %+v", payload.AvatarFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplyPlanToStateKeepsOnlyResourceIDsAndPayloadUsesLookup(t *testing.T) {
|
||||||
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
||||||
|
plan := purchasePlan{
|
||||||
|
TargetLevel: 3,
|
||||||
|
StartAt: now,
|
||||||
|
ExpireAt: now.AddDate(0, 0, vipDurationDay),
|
||||||
|
DurationDays: vipDurationDay,
|
||||||
|
TargetConfig: model.VipLevelConfig{
|
||||||
|
Level: 3,
|
||||||
|
DisplayName: "VIP 3",
|
||||||
|
AvatarFrameResourceID: 11,
|
||||||
|
AvatarFrameName: "frame",
|
||||||
|
AvatarFrameURL: "https://example.com/frame.svga",
|
||||||
|
EntryEffectResourceID: 12,
|
||||||
|
EntryEffectName: "ride",
|
||||||
|
EntryEffectURL: "https://example.com/ride.svga",
|
||||||
|
ChatBubbleResourceID: 13,
|
||||||
|
ChatBubbleName: "bubble",
|
||||||
|
ChatBubbleURL: "https://example.com/bubble.svga",
|
||||||
|
FloatPictureResourceID: 14,
|
||||||
|
FloatPictureName: "float",
|
||||||
|
FloatPictureURL: "https://example.com/float.svga",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
state := &model.UserVipState{UserID: 1001, SysOrigin: "LIKEI"}
|
||||||
|
applyPlanToState(state, plan, now)
|
||||||
|
|
||||||
|
if state.AvatarFrameResourceID != plan.TargetConfig.AvatarFrameResourceID ||
|
||||||
|
state.EntryEffectResourceID != plan.TargetConfig.EntryEffectResourceID ||
|
||||||
|
state.ChatBubbleResourceID != plan.TargetConfig.ChatBubbleResourceID ||
|
||||||
|
state.FloatPictureResourceID != plan.TargetConfig.FloatPictureResourceID {
|
||||||
|
t.Fatalf("state resource ids were not copied from target config")
|
||||||
|
}
|
||||||
|
if state.AvatarFrameName != "" || state.AvatarFrameURL != "" {
|
||||||
|
t.Fatalf("state stored resource details: name=%q url=%q", state.AvatarFrameName, state.AvatarFrameURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := statePayloadFromModelWithResources(state, now, vipResourceLookup{
|
||||||
|
11: {ID: 11, Name: "frame", Cover: "https://example.com/frame.png", SourceURL: "https://example.com/frame.svga"},
|
||||||
|
12: {ID: 12, Name: "ride", Cover: "https://example.com/ride.png", SourceURL: "https://example.com/ride.svga"},
|
||||||
|
13: {ID: 13, Name: "bubble", Cover: "https://example.com/bubble.png", SourceURL: "https://example.com/bubble.svga"},
|
||||||
|
14: {ID: 14, Name: "float", Cover: "https://example.com/float.png", SourceURL: "https://example.com/float.svga"},
|
||||||
|
})
|
||||||
|
if payload.AvatarFrame.Cover != "https://example.com/frame.png" ||
|
||||||
|
payload.EntryEffect.Cover != "https://example.com/ride.png" ||
|
||||||
|
payload.ChatBubble.Cover != "https://example.com/bubble.png" ||
|
||||||
|
payload.FloatPicture.Cover != "https://example.com/float.png" {
|
||||||
|
t.Fatalf("state payload covers were not loaded from resource lookup")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMapWalletPurchaseErrorInsufficientBalance(t *testing.T) {
|
func TestMapWalletPurchaseErrorInsufficientBalance(t *testing.T) {
|
||||||
err := errors.New(`java api failed: status=406 body={"errorCode":5000,"errorCodeName":"INSUFFICIENT_BALANCE","errorMsg":"balance not made"}`)
|
err := errors.New(`java api failed: status=406 body={"errorCode":5000,"errorCodeName":"INSUFFICIENT_BALANCE","errorMsg":"balance not made"}`)
|
||||||
|
|
||||||
|
|||||||
95
internal/service/vip/resources.go
Normal file
95
internal/service/vip/resources.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package vip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"chatapp3-golang/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type vipResourceRecord struct {
|
||||||
|
ID int64 `gorm:"column:id"`
|
||||||
|
Name string `gorm:"column:name"`
|
||||||
|
Cover string `gorm:"column:cover"`
|
||||||
|
SourceURL string `gorm:"column:source_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type vipResourceLookup map[int64]vipResourceRecord
|
||||||
|
|
||||||
|
func (s *Service) loadResourceLookup(ctx context.Context, resourceIDs []int64) (vipResourceLookup, error) {
|
||||||
|
ids := compactResourceIDs(resourceIDs)
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows []vipResourceRecord
|
||||||
|
if err := s.db.WithContext(ctx).
|
||||||
|
Table("props_source_record").
|
||||||
|
Select("id, name, cover, source_url").
|
||||||
|
Where("id IN ?", ids).
|
||||||
|
Find(&rows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup := make(vipResourceLookup, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
lookup[row.ID] = row
|
||||||
|
}
|
||||||
|
return lookup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compactResourceIDs(resourceIDs []int64) []int64 {
|
||||||
|
seen := make(map[int64]struct{}, len(resourceIDs))
|
||||||
|
ids := make([]int64, 0, len(resourceIDs))
|
||||||
|
for _, id := range resourceIDs {
|
||||||
|
if id <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := seen[id]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[id] = struct{}{}
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceIDsFromConfigRows(rows []model.VipLevelConfig) []int64 {
|
||||||
|
ids := make([]int64, 0, len(rows)*5)
|
||||||
|
for _, row := range rows {
|
||||||
|
ids = appendResourceIDsFromConfig(ids, row)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceIDsFromConfigMap(configs map[int]model.VipLevelConfig) []int64 {
|
||||||
|
ids := make([]int64, 0, len(configs)*5)
|
||||||
|
for _, row := range configs {
|
||||||
|
ids = appendResourceIDsFromConfig(ids, row)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendResourceIDsFromConfig(ids []int64, row model.VipLevelConfig) []int64 {
|
||||||
|
return append(
|
||||||
|
ids,
|
||||||
|
row.BadgeResourceID,
|
||||||
|
row.AvatarFrameResourceID,
|
||||||
|
row.EntryEffectResourceID,
|
||||||
|
row.ChatBubbleResourceID,
|
||||||
|
row.FloatPictureResourceID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendResourceIDsFromState(ids []int64, row *model.UserVipState) []int64 {
|
||||||
|
if row == nil {
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
return append(
|
||||||
|
ids,
|
||||||
|
row.BadgeResourceID,
|
||||||
|
row.AvatarFrameResourceID,
|
||||||
|
row.EntryEffectResourceID,
|
||||||
|
row.ChatBubbleResourceID,
|
||||||
|
row.FloatPictureResourceID,
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -103,6 +103,8 @@ type VipResourcePayload struct {
|
|||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
SourceURL string `json:"sourceUrl,omitempty"`
|
SourceURL string `json:"sourceUrl,omitempty"`
|
||||||
ResourceURL string `json:"resourceUrl,omitempty"`
|
ResourceURL string `json:"resourceUrl,omitempty"`
|
||||||
|
Cover string `json:"cover,omitempty"`
|
||||||
|
CoverURL string `json:"coverUrl,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceID int64
|
type ResourceID int64
|
||||||
@ -311,18 +313,36 @@ func defaultLevelPayload(level int) VipLevelPayload {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcePayload(resourceID int64, name string, url string) VipResourcePayload {
|
func resourcePayload(resourceID int64, name string, url string, cover string) VipResourcePayload {
|
||||||
url = strings.TrimSpace(url)
|
url = strings.TrimSpace(url)
|
||||||
|
cover = strings.TrimSpace(cover)
|
||||||
return VipResourcePayload{
|
return VipResourcePayload{
|
||||||
ResourceID: ResourceID(resourceID),
|
ResourceID: ResourceID(resourceID),
|
||||||
Name: strings.TrimSpace(name),
|
Name: strings.TrimSpace(name),
|
||||||
URL: url,
|
URL: url,
|
||||||
SourceURL: url,
|
SourceURL: url,
|
||||||
ResourceURL: url,
|
ResourceURL: url,
|
||||||
|
Cover: cover,
|
||||||
|
CoverURL: cover,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourcePayloadWithLookup(resourceID int64, name string, url string, cover string, resources vipResourceLookup) VipResourcePayload {
|
||||||
|
if resourceID > 0 {
|
||||||
|
if record, exists := resources[resourceID]; exists {
|
||||||
|
name = record.Name
|
||||||
|
url = record.SourceURL
|
||||||
|
cover = record.Cover
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resourcePayload(resourceID, name, url, cover)
|
||||||
|
}
|
||||||
|
|
||||||
func payloadFromConfig(row model.VipLevelConfig) VipLevelPayload {
|
func payloadFromConfig(row model.VipLevelConfig) VipLevelPayload {
|
||||||
|
return payloadFromConfigWithResources(row, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func payloadFromConfigWithResources(row model.VipLevelConfig, resources vipResourceLookup) VipLevelPayload {
|
||||||
duration := row.DurationDays
|
duration := row.DurationDays
|
||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
duration = vipDurationDay
|
duration = vipDurationDay
|
||||||
@ -343,16 +363,20 @@ func payloadFromConfig(row model.VipLevelConfig) VipLevelPayload {
|
|||||||
Enabled: row.Enabled,
|
Enabled: row.Enabled,
|
||||||
DurationDays: duration,
|
DurationDays: duration,
|
||||||
PriceGold: row.PriceGold,
|
PriceGold: row.PriceGold,
|
||||||
Badge: resourcePayload(row.BadgeResourceID, row.BadgeName, row.BadgeURL),
|
Badge: resourcePayloadWithLookup(row.BadgeResourceID, row.BadgeName, row.BadgeURL, "", resources),
|
||||||
AvatarFrame: resourcePayload(row.AvatarFrameResourceID, row.AvatarFrameName, row.AvatarFrameURL),
|
AvatarFrame: resourcePayloadWithLookup(row.AvatarFrameResourceID, row.AvatarFrameName, row.AvatarFrameURL, "", resources),
|
||||||
EntryEffect: resourcePayload(row.EntryEffectResourceID, row.EntryEffectName, row.EntryEffectURL),
|
EntryEffect: resourcePayloadWithLookup(row.EntryEffectResourceID, row.EntryEffectName, row.EntryEffectURL, "", resources),
|
||||||
ChatBubble: resourcePayload(row.ChatBubbleResourceID, row.ChatBubbleName, row.ChatBubbleURL),
|
ChatBubble: resourcePayloadWithLookup(row.ChatBubbleResourceID, row.ChatBubbleName, row.ChatBubbleURL, "", resources),
|
||||||
FloatPicture: resourcePayload(row.FloatPictureResourceID, row.FloatPictureName, row.FloatPictureURL),
|
FloatPicture: resourcePayloadWithLookup(row.FloatPictureResourceID, row.FloatPictureName, row.FloatPictureURL, "", resources),
|
||||||
UpdateTime: formatTime(row.UpdateTime),
|
UpdateTime: formatTime(row.UpdateTime),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func statePayloadFromModel(row *model.UserVipState, now time.Time) UserVipStatePayload {
|
func statePayloadFromModel(row *model.UserVipState, now time.Time) UserVipStatePayload {
|
||||||
|
return statePayloadFromModelWithResources(row, now, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statePayloadFromModelWithResources(row *model.UserVipState, now time.Time, resources vipResourceLookup) UserVipStatePayload {
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return UserVipStatePayload{Active: false, Status: statusExpired}
|
return UserVipStatePayload{Active: false, Status: statusExpired}
|
||||||
}
|
}
|
||||||
@ -377,11 +401,11 @@ func statePayloadFromModel(row *model.UserVipState, now time.Time) UserVipStateP
|
|||||||
payload.DisplayName = ""
|
payload.DisplayName = ""
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
payload.Badge = resourcePayload(row.BadgeResourceID, row.BadgeName, row.BadgeURL)
|
payload.Badge = resourcePayloadWithLookup(row.BadgeResourceID, row.BadgeName, row.BadgeURL, "", resources)
|
||||||
payload.AvatarFrame = resourcePayload(row.AvatarFrameResourceID, row.AvatarFrameName, row.AvatarFrameURL)
|
payload.AvatarFrame = resourcePayloadWithLookup(row.AvatarFrameResourceID, row.AvatarFrameName, row.AvatarFrameURL, "", resources)
|
||||||
payload.EntryEffect = resourcePayload(row.EntryEffectResourceID, row.EntryEffectName, row.EntryEffectURL)
|
payload.EntryEffect = resourcePayloadWithLookup(row.EntryEffectResourceID, row.EntryEffectName, row.EntryEffectURL, "", resources)
|
||||||
payload.ChatBubble = resourcePayload(row.ChatBubbleResourceID, row.ChatBubbleName, row.ChatBubbleURL)
|
payload.ChatBubble = resourcePayloadWithLookup(row.ChatBubbleResourceID, row.ChatBubbleName, row.ChatBubbleURL, "", resources)
|
||||||
payload.FloatPicture = resourcePayload(row.FloatPictureResourceID, row.FloatPictureName, row.FloatPictureURL)
|
payload.FloatPicture = resourcePayloadWithLookup(row.FloatPictureResourceID, row.FloatPictureName, row.FloatPictureURL, "", resources)
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user