feat: allow manager center VIP4 and VIP5 gifts

This commit is contained in:
hy001 2026-07-07 19:58:23 +08:00
parent 4bb8613570
commit 714c1c5c68
3 changed files with 49 additions and 24 deletions

View File

@ -759,7 +759,7 @@ func (s *Service) loadGiftableNobleVIPAbility(ctx context.Context, propsID int64
return propsNobleVIPAbilityRow{}, serverError("vip_ability_query_failed", err.Error()) return propsNobleVIPAbilityRow{}, serverError("vip_ability_query_failed", err.Error())
} }
if row.VIPLevel > maxGiftableVIPLevel { if row.VIPLevel > maxGiftableVIPLevel {
return propsNobleVIPAbilityRow{}, badRequest("vip_not_giftable", "only noble VIP level 1-3 can be gifted") return propsNobleVIPAbilityRow{}, badRequest("vip_not_giftable", "only noble VIP level 1-5 can be gifted")
} }
return row, nil return row, nil
} }
@ -857,7 +857,7 @@ func propsRowToView(row propsSourceRecordRow, vipLevels map[int64]int) (PropsVie
if row.Type == propsTypeNobleVIP { if row.Type == propsTypeNobleVIP {
var exists bool var exists bool
level, exists = vipLevels[row.ID] level, exists = vipLevels[row.ID]
if !exists || level > 3 { if !exists || level > maxGiftableVIPLevel {
return PropsView{}, false return PropsView{}, false
} }
} }

View File

@ -107,10 +107,14 @@ func TestListPropsRequiresAdminFreeAndFiltersVIPLevels(t *testing.T) {
propsSourceRecordRow{ID: 102, Type: propsTypeRide, Name: "Paid ride", AdminFree: false}, propsSourceRecordRow{ID: 102, Type: propsTypeRide, Name: "Paid ride", AdminFree: false},
propsSourceRecordRow{ID: 201, Type: propsTypeNobleVIP, Name: "VIP 3", AdminFree: true}, propsSourceRecordRow{ID: 201, Type: propsTypeNobleVIP, Name: "VIP 3", AdminFree: true},
propsSourceRecordRow{ID: 204, Type: propsTypeNobleVIP, Name: "VIP 4", AdminFree: true}, propsSourceRecordRow{ID: 204, Type: propsTypeNobleVIP, Name: "VIP 4", AdminFree: true},
propsSourceRecordRow{ID: 205, Type: propsTypeNobleVIP, Name: "VIP 5", AdminFree: true},
propsSourceRecordRow{ID: 206, Type: propsTypeNobleVIP, Name: "VIP 6", AdminFree: true},
) )
seedAbilities(t, service.db.(*gorm.DB), seedAbilities(t, service.db.(*gorm.DB),
propsNobleVIPAbilityRow{ID: 201, VIPLevel: 3}, propsNobleVIPAbilityRow{ID: 201, VIPLevel: 3},
propsNobleVIPAbilityRow{ID: 204, VIPLevel: 4}, propsNobleVIPAbilityRow{ID: 204, VIPLevel: 4},
propsNobleVIPAbilityRow{ID: 205, VIPLevel: 5},
propsNobleVIPAbilityRow{ID: 206, VIPLevel: 6},
) )
resp, err := service.ListProps(context.Background(), AuthUser{UserID: 1}, "") resp, err := service.ListProps(context.Background(), AuthUser{UserID: 1}, "")
@ -129,10 +133,16 @@ func TestListPropsRequiresAdminFreeAndFiltersVIPLevels(t *testing.T) {
t.Fatalf("records = %+v, non-admin-free props must be hidden", resp.Records) t.Fatalf("records = %+v, non-admin-free props must be hidden", resp.Records)
} }
if item, ok := got[201]; !ok || item.Days != defaultVIPGiftDays || item.VIPLevel != 3 { if item, ok := got[201]; !ok || item.Days != defaultVIPGiftDays || item.VIPLevel != 3 {
t.Fatalf("vip 201 = %+v, want level 3 with 3 days", item) t.Fatalf("vip 201 = %+v, want level 3 with default VIP days", item)
} }
if _, ok := got[204]; ok { if item, ok := got[204]; !ok || item.VIPLevel != 4 {
t.Fatalf("records = %+v, VIP level 4 must be hidden", resp.Records) t.Fatalf("vip 204 = %+v, want level 4 visible", item)
}
if item, ok := got[205]; !ok || item.VIPLevel != 5 {
t.Fatalf("vip 205 = %+v, want level 5 visible", item)
}
if _, ok := got[206]; ok {
t.Fatalf("records = %+v, VIP level 6 must be hidden", resp.Records)
} }
} }
@ -145,18 +155,32 @@ func TestListPropsReturnsVIPLevelConfigs(t *testing.T) {
model.VipLevelConfig{ID: 302, SysOrigin: "LIKEI", Level: 2, LevelCode: "VIP2", DisplayName: "VIP 2", Enabled: false, DurationDays: 30}, model.VipLevelConfig{ID: 302, SysOrigin: "LIKEI", Level: 2, LevelCode: "VIP2", DisplayName: "VIP 2", Enabled: false, DurationDays: 30},
model.VipLevelConfig{ID: 303, SysOrigin: "ATYOU", Level: 1, LevelCode: "VIP1", DisplayName: "ATYOU VIP 1", Enabled: true, DurationDays: 30}, model.VipLevelConfig{ID: 303, SysOrigin: "ATYOU", Level: 1, LevelCode: "VIP1", DisplayName: "ATYOU VIP 1", Enabled: true, DurationDays: 30},
model.VipLevelConfig{ID: 304, SysOrigin: "LIKEI", Level: 4, LevelCode: "VIP4", DisplayName: "VIP 4", Enabled: true, DurationDays: 30}, model.VipLevelConfig{ID: 304, SysOrigin: "LIKEI", Level: 4, LevelCode: "VIP4", DisplayName: "VIP 4", Enabled: true, DurationDays: 30},
model.VipLevelConfig{ID: 305, SysOrigin: "LIKEI", Level: 5, LevelCode: "VIP5", DisplayName: "VIP 5", Enabled: true, DurationDays: 30},
model.VipLevelConfig{ID: 306, SysOrigin: "LIKEI", Level: 6, LevelCode: "VIP6", DisplayName: "VIP 6", Enabled: true, DurationDays: 30},
) )
resp, err := service.ListProps(context.Background(), AuthUser{UserID: 1, SysOrigin: "LIKEI"}, propsTypeNobleVIP) resp, err := service.ListProps(context.Background(), AuthUser{UserID: 1, SysOrigin: "LIKEI"}, propsTypeNobleVIP)
if err != nil { if err != nil {
t.Fatalf("ListProps() error = %v", err) t.Fatalf("ListProps() error = %v", err)
} }
if len(resp.Records) != 1 { if len(resp.Records) != 3 {
t.Fatalf("records = %+v, want only one enabled LIKEI vip config", resp.Records) t.Fatalf("records = %+v, want enabled LIKEI vip configs up to level 5", resp.Records)
} }
got := resp.Records[0] got := map[int64]PropsView{}
if got.ID.Int64() != 301 || got.Type != propsTypeNobleVIP || got.VIPLevel != 1 || got.Days != 30 { for _, item := range resp.Records {
t.Fatalf("vip props = %+v, want config 301 level 1", got) got[item.ID.Int64()] = item
}
if item := got[301]; item.Type != propsTypeNobleVIP || item.VIPLevel != 1 || item.Days != 30 {
t.Fatalf("vip props 301 = %+v, want config 301 level 1", item)
}
if item := got[304]; item.Type != propsTypeNobleVIP || item.VIPLevel != 4 || item.Days != 30 {
t.Fatalf("vip props 304 = %+v, want config 304 level 4", item)
}
if item := got[305]; item.Type != propsTypeNobleVIP || item.VIPLevel != 5 || item.Days != 30 {
t.Fatalf("vip props 305 = %+v, want config 305 level 5", item)
}
if _, ok := got[306]; ok {
t.Fatalf("records = %+v, VIP level 6 must be hidden", resp.Records)
} }
} }
@ -398,12 +422,12 @@ func TestSendVIPUsesVIPLevelConfig(t *testing.T) {
seedVIPConfigs(t, db, model.VipLevelConfig{ seedVIPConfigs(t, db, model.VipLevelConfig{
ID: 301, ID: 301,
SysOrigin: "LIKEI", SysOrigin: "LIKEI",
Level: 3, Level: 5,
LevelCode: "VIP3", LevelCode: "VIP5",
DisplayName: "VIP 3", DisplayName: "VIP 5",
Enabled: true, Enabled: true,
DurationDays: 30, DurationDays: 30,
PriceGold: 3000, PriceGold: 5000,
}) })
resp, err := service.SendProps(context.Background(), AuthUser{UserID: 1, SysOrigin: "LIKEI"}, SendPropsRequest{ resp, err := service.SendProps(context.Background(), AuthUser{UserID: 1, SysOrigin: "LIKEI"}, SendPropsRequest{
@ -413,7 +437,7 @@ func TestSendVIPUsesVIPLevelConfig(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("SendProps() error = %v", err) t.Fatalf("SendProps() error = %v", err)
} }
if !resp.Success || resp.Type != propsTypeNobleVIP || resp.VIPLevel != 3 || resp.Days != 30 { if !resp.Success || resp.Type != propsTypeNobleVIP || resp.VIPLevel != 5 || resp.Days != 30 {
t.Fatalf("resp = %+v, want vip gift success", resp) t.Fatalf("resp = %+v, want vip gift success", resp)
} }
@ -421,8 +445,8 @@ func TestSendVIPUsesVIPLevelConfig(t *testing.T) {
if err := db.Where("sys_origin = ? AND user_id = ?", "LIKEI", int64(2)).First(&state).Error; err != nil { if err := db.Where("sys_origin = ? AND user_id = ?", "LIKEI", int64(2)).First(&state).Error; err != nil {
t.Fatalf("load state: %v", err) t.Fatalf("load state: %v", err)
} }
if state.Level != 3 || state.Status != vipStatusActive { if state.Level != 5 || state.Status != vipStatusActive {
t.Fatalf("state = %+v, want vip3 active", state) t.Fatalf("state = %+v, want vip5 active", state)
} }
var orders int64 var orders int64
if err := db.Model(&model.VipOrderRecord{}).Where("user_id = ? AND order_type = ?", int64(2), vipOrderTypeGMGift).Count(&orders).Error; err != nil { if err := db.Model(&model.VipOrderRecord{}).Where("user_id = ? AND order_type = ?", int64(2), vipOrderTypeGMGift).Count(&orders).Error; err != nil {
@ -432,7 +456,7 @@ func TestSendVIPUsesVIPLevelConfig(t *testing.T) {
t.Fatalf("orders = %d, want 1", orders) t.Fatalf("orders = %d, want 1", orders)
} }
var records int64 var records int64
if err := db.Model(&gmVIPGiftRecordRow{}).Where("user_id = ? AND vip_level = ?", int64(2), 3).Count(&records).Error; err != nil { if err := db.Model(&gmVIPGiftRecordRow{}).Where("user_id = ? AND vip_level = ?", int64(2), 5).Count(&records).Error; err != nil {
t.Fatalf("count gift records: %v", err) t.Fatalf("count gift records: %v", err)
} }
if records != 1 { if records != 1 {
@ -493,9 +517,9 @@ func TestSendVIPRejectsLevelAboveGiftLimit(t *testing.T) {
seedVIPConfigs(t, db, model.VipLevelConfig{ seedVIPConfigs(t, db, model.VipLevelConfig{
ID: 304, ID: 304,
SysOrigin: "LIKEI", SysOrigin: "LIKEI",
Level: 4, Level: 6,
LevelCode: "VIP4", LevelCode: "VIP6",
DisplayName: "VIP 4", DisplayName: "VIP 6",
Enabled: true, Enabled: true,
DurationDays: 30, DurationDays: 30,
}) })

View File

@ -28,9 +28,10 @@ const (
vipGiftOrigin = "VIP_PURCHASE" vipGiftOrigin = "VIP_PURCHASE"
vipGiftOriginDesc = "VIP purchase" vipGiftOriginDesc = "VIP purchase"
defaultGiftDays = 7 defaultGiftDays = 7
defaultVIPGiftDays = 30 defaultVIPGiftDays = 30
maxGiftableVIPLevel = 3 // 经理中心 VIP 赠送按当前运营范围放开 VIP1-VIP5列表和发放校验共用该上限避免展示可选但提交失败。
maxGiftableVIPLevel = 5
accountStatusNormal = "NORMAL" accountStatusNormal = "NORMAL"
accountStatusFreeze = "FREEZE" accountStatusFreeze = "FREEZE"