582 lines
21 KiB
Go
582 lines
21 KiB
Go
package vipconfig
|
||
|
||
import (
|
||
"strconv"
|
||
"strings"
|
||
|
||
"hyapp-admin-server/internal/appctx"
|
||
"hyapp-admin-server/internal/integration/walletclient"
|
||
"hyapp-admin-server/internal/middleware"
|
||
"hyapp-admin-server/internal/modules/shared"
|
||
"hyapp-admin-server/internal/response"
|
||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
type Handler struct {
|
||
wallet walletclient.Client
|
||
audit shared.OperationLogger
|
||
}
|
||
|
||
func New(wallet walletclient.Client, audit shared.OperationLogger) *Handler {
|
||
return &Handler{wallet: wallet, audit: audit}
|
||
}
|
||
|
||
type configRequest struct {
|
||
Levels []vipLevelDTO `json:"levels"`
|
||
}
|
||
|
||
type programRequest struct {
|
||
Config *vipProgramConfigDTO `json:"config"`
|
||
}
|
||
|
||
type grantVipRequest struct {
|
||
CommandID string `json:"commandId"`
|
||
TargetUserID any `json:"targetUserId"`
|
||
Level int32 `json:"level"`
|
||
Reason string `json:"reason"`
|
||
}
|
||
|
||
type grantVipTrialCardRequest struct {
|
||
CommandID string `json:"commandId"`
|
||
TargetUserID any `json:"targetUserId"`
|
||
Level int32 `json:"level"`
|
||
DurationMS int64 `json:"durationMs"`
|
||
ResourceID *int64 `json:"resourceId"`
|
||
Reason string `json:"reason"`
|
||
}
|
||
|
||
type configDTO struct {
|
||
Levels []vipLevelDTO `json:"levels"`
|
||
ProgramConfig vipProgramConfigDTO `json:"programConfig"`
|
||
ServerTimeMS int64 `json:"serverTimeMs"`
|
||
}
|
||
|
||
type programDTO struct {
|
||
Config vipProgramConfigDTO `json:"config"`
|
||
Benefits []vipBenefitDTO `json:"benefits"`
|
||
ServerTimeMS int64 `json:"serverTimeMs"`
|
||
}
|
||
|
||
type grantVipDTO struct {
|
||
TransactionID string `json:"transactionId"`
|
||
VIP userVipDTO `json:"vip"`
|
||
RewardItems []vipRewardItemDTO `json:"rewardItems"`
|
||
ServerTimeMS int64 `json:"serverTimeMs"`
|
||
}
|
||
|
||
type grantVipTrialCardDTO struct {
|
||
TrialCard vipTrialCardDTO `json:"trialCard"`
|
||
State vipStateDTO `json:"state"`
|
||
ServerTimeMS int64 `json:"serverTimeMs"`
|
||
}
|
||
|
||
type userVipDTO struct {
|
||
UserID string `json:"userId"`
|
||
Level int32 `json:"level"`
|
||
Name string `json:"name"`
|
||
Active bool `json:"active"`
|
||
StartedAtMS int64 `json:"startedAtMs"`
|
||
ExpiresAtMS int64 `json:"expiresAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
ProgramType string `json:"programType"`
|
||
ConfigVersion int64 `json:"configVersion"`
|
||
}
|
||
|
||
type vipLevelDTO struct {
|
||
Level int32 `json:"level"`
|
||
Name string `json:"name"`
|
||
Status string `json:"status"`
|
||
PriceCoin int64 `json:"priceCoin"`
|
||
DurationMS int64 `json:"durationMs"`
|
||
RewardResourceGroupID int64 `json:"rewardResourceGroupId"`
|
||
SortOrder int32 `json:"sortOrder"`
|
||
RechargeGateRequired bool `json:"rechargeGateRequired"`
|
||
RequiredRechargeCoinAmount int64 `json:"requiredRechargeCoinAmount"`
|
||
UserRechargeCoinAmount int64 `json:"userRechargeCoinAmount"`
|
||
PurchaseLockedReason string `json:"purchaseLockedReason"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
Benefits []vipBenefitDTO `json:"benefits"`
|
||
ConfigVersion int64 `json:"configVersion"`
|
||
}
|
||
|
||
type vipProgramConfigDTO struct {
|
||
AppCode string `json:"appCode"`
|
||
ProgramType string `json:"programType"`
|
||
LevelCount int32 `json:"levelCount"`
|
||
SameLevelExpiryPolicy string `json:"sameLevelExpiryPolicy"`
|
||
UpgradeExpiryPolicy string `json:"upgradeExpiryPolicy"`
|
||
DowngradePurchasePolicy string `json:"downgradePurchasePolicy"`
|
||
BenefitInheritancePolicy string `json:"benefitInheritancePolicy"`
|
||
GrantMode string `json:"grantMode"`
|
||
TrialCardEnabled bool `json:"trialCardEnabled"`
|
||
Status string `json:"status"`
|
||
ConfigVersion int64 `json:"configVersion"`
|
||
UpdatedByAdminID int64 `json:"updatedByAdminId"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
}
|
||
|
||
type vipBenefitDTO struct {
|
||
BenefitCode string `json:"benefitCode"`
|
||
Name string `json:"name"`
|
||
BenefitType string `json:"benefitType"`
|
||
UnlockLevel int32 `json:"unlockLevel"`
|
||
Status string `json:"status"`
|
||
TrialEnabled bool `json:"trialEnabled"`
|
||
ResourceID int64 `json:"resourceId"`
|
||
ResourceType string `json:"resourceType"`
|
||
ExecutionScope string `json:"executionScope"`
|
||
AutoEquip bool `json:"autoEquip"`
|
||
SortOrder int32 `json:"sortOrder"`
|
||
MetadataJSON string `json:"metadataJson"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
}
|
||
|
||
type vipTrialCardDTO struct {
|
||
TrialCardID string `json:"trialCardId"`
|
||
EntitlementID string `json:"entitlementId"`
|
||
ResourceID int64 `json:"resourceId"`
|
||
UserID string `json:"userId"`
|
||
Level int32 `json:"level"`
|
||
Name string `json:"name"`
|
||
Status string `json:"status"`
|
||
Equipped bool `json:"equipped"`
|
||
DurationMS int64 `json:"durationMs"`
|
||
EffectiveAtMS int64 `json:"effectiveAtMs"`
|
||
ExpiresAtMS int64 `json:"expiresAtMs"`
|
||
RemainingDurationMS int64 `json:"remainingDurationMs"`
|
||
GrantSource string `json:"grantSource"`
|
||
SourceGrantID string `json:"sourceGrantId"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
}
|
||
|
||
type vipStateDTO struct {
|
||
PaidVIP userVipDTO `json:"paidVip"`
|
||
EquippedTrialCard vipTrialCardDTO `json:"equippedTrialCard"`
|
||
EffectiveVIP userVipDTO `json:"effectiveVip"`
|
||
EffectiveSource string `json:"effectiveSource"`
|
||
EffectiveBenefits []vipBenefitDTO `json:"effectiveBenefits"`
|
||
EvaluatedAtMS int64 `json:"evaluatedAtMs"`
|
||
ProgramConfig vipProgramConfigDTO `json:"programConfig"`
|
||
}
|
||
|
||
type vipRewardItemDTO struct {
|
||
ResourceID string `json:"resourceId"`
|
||
ResourceCode string `json:"resourceCode"`
|
||
ResourceType string `json:"resourceType"`
|
||
Name string `json:"name"`
|
||
Quantity int64 `json:"quantity"`
|
||
ExpiresAtMS int64 `json:"expiresAtMs"`
|
||
}
|
||
|
||
func (h *Handler) GetProgram(c *gin.Context) {
|
||
resp, err := h.wallet.GetVipProgramConfig(c.Request.Context(), &walletv1.GetVipProgramConfigRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取 VIP 体系失败")
|
||
return
|
||
}
|
||
// 体系规则与权益目录同次返回,保证后台切换 App 后不会把上一 App 的权益矩阵误带入当前表单。
|
||
response.OK(c, programDTO{
|
||
Config: vipProgramConfigFromProto(resp.GetConfig()),
|
||
Benefits: vipBenefitsFromProto(resp.GetBenefits()),
|
||
ServerTimeMS: resp.GetServerTimeMs(),
|
||
})
|
||
}
|
||
|
||
func (h *Handler) UpdateProgram(c *gin.Context) {
|
||
var req programRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil || req.Config == nil {
|
||
response.BadRequest(c, "VIP 体系参数不正确")
|
||
return
|
||
}
|
||
config := req.Config.toProto()
|
||
if config.GetLevelCount() <= 0 || strings.TrimSpace(config.GetProgramType()) == "" || strings.TrimSpace(config.GetStatus()) == "" {
|
||
response.BadRequest(c, "VIP 体系参数不正确")
|
||
return
|
||
}
|
||
appCode := appctx.FromContext(c.Request.Context())
|
||
// AppCode、版本和审计操作者只取服务端上下文;即使客户端提交同名字段也不能跨 App 或伪造审计信息。
|
||
config.AppCode = appCode
|
||
config.ConfigVersion = 0
|
||
config.UpdatedByAdminId = 0
|
||
config.CreatedAtMs = 0
|
||
config.UpdatedAtMs = 0
|
||
resp, err := h.wallet.UpdateAdminVipProgramConfig(c.Request.Context(), &walletv1.UpdateAdminVipProgramConfigRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appCode,
|
||
Config: config,
|
||
OperatorUserId: int64(middleware.CurrentUserID(c)),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
shared.OperationLogWithResourceID(c, h.audit, "update-vip-program", "vip_program_configs", appCode, "success", resp.GetConfig().GetProgramType())
|
||
response.OK(c, programDTO{
|
||
Config: vipProgramConfigFromProto(resp.GetConfig()),
|
||
Benefits: []vipBenefitDTO{},
|
||
ServerTimeMS: resp.GetServerTimeMs(),
|
||
})
|
||
}
|
||
|
||
func (h *Handler) ListLevels(c *gin.Context) {
|
||
resp, err := h.wallet.ListAdminVipLevels(c.Request.Context(), &walletv1.ListAdminVipLevelsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取 VIP 配置失败")
|
||
return
|
||
}
|
||
response.OK(c, configDTO{
|
||
Levels: vipLevelsFromProto(resp.GetLevels()),
|
||
ProgramConfig: vipProgramConfigFromProto(resp.GetProgramConfig()),
|
||
ServerTimeMS: resp.GetServerTimeMs(),
|
||
})
|
||
}
|
||
|
||
func (h *Handler) GrantVIP(c *gin.Context) {
|
||
var req grantVipRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "VIP 赠送参数不正确")
|
||
return
|
||
}
|
||
targetUserID, ok := parseFlexibleUserID(req.TargetUserID)
|
||
if !ok || req.Level <= 0 {
|
||
response.BadRequest(c, "VIP 赠送参数不正确")
|
||
return
|
||
}
|
||
reason := strings.TrimSpace(req.Reason)
|
||
if reason == "" {
|
||
response.BadRequest(c, "赠送原因不能为空")
|
||
return
|
||
}
|
||
commandID := strings.TrimSpace(req.CommandID)
|
||
if commandID == "" {
|
||
commandID = "admin_vip_grant:" + middleware.CurrentRequestID(c)
|
||
}
|
||
resp, err := h.wallet.GrantVip(c.Request.Context(), &walletv1.GrantVipRequest{
|
||
CommandId: commandID,
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
TargetUserId: targetUserID,
|
||
Level: req.Level,
|
||
GrantSource: "admin_grant",
|
||
OperatorUserId: int64(middleware.CurrentUserID(c)),
|
||
Reason: reason,
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
shared.OperationLogWithResourceID(c, h.audit, "grant-vip", "user_vip_memberships", strconv.FormatInt(targetUserID, 10), "success", "level="+strconv.Itoa(int(req.Level)))
|
||
response.Created(c, grantVipFromProto(resp))
|
||
}
|
||
|
||
func (h *Handler) GrantVIPTrialCard(c *gin.Context) {
|
||
var req grantVipTrialCardRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "VIP 体验卡赠送参数不正确")
|
||
return
|
||
}
|
||
targetUserID, ok := parseFlexibleUserID(req.TargetUserID)
|
||
if !ok || req.Level <= 0 || req.DurationMS <= 0 || (req.ResourceID != nil && *req.ResourceID <= 0) {
|
||
response.BadRequest(c, "VIP 体验卡赠送参数不正确")
|
||
return
|
||
}
|
||
reason := strings.TrimSpace(req.Reason)
|
||
if reason == "" {
|
||
response.BadRequest(c, "赠送原因不能为空")
|
||
return
|
||
}
|
||
commandID := strings.TrimSpace(req.CommandID)
|
||
if commandID == "" {
|
||
commandID = "admin_vip_trial_card_grant:" + middleware.CurrentRequestID(c)
|
||
}
|
||
resp, err := h.wallet.GrantVipTrialCard(c.Request.Context(), &walletv1.GrantVipTrialCardRequest{
|
||
CommandId: commandID,
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
TargetUserId: targetUserID,
|
||
Level: req.Level,
|
||
DurationMs: req.DurationMS,
|
||
ResourceId: req.ResourceID,
|
||
GrantSource: "admin_grant",
|
||
OperatorUserId: int64(middleware.CurrentUserID(c)),
|
||
Reason: reason,
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
// 体验卡是独立背包实例,审计目标使用 trial_card_id,后续切换佩戴不会覆盖本次赠送记录。
|
||
shared.OperationLogWithResourceID(c, h.audit, "grant-vip-trial-card", "user_vip_trial_cards", resp.GetTrialCard().GetTrialCardId(), "success", "target_user_id="+strconv.FormatInt(targetUserID, 10))
|
||
response.Created(c, grantVipTrialCardFromProto(resp))
|
||
}
|
||
|
||
func (h *Handler) UpdateLevels(c *gin.Context) {
|
||
var req configRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "VIP 配置参数不正确")
|
||
return
|
||
}
|
||
input := make([]*walletv1.AdminVipLevelInput, 0, len(req.Levels))
|
||
for _, level := range req.Levels {
|
||
benefits := make([]*walletv1.VipBenefit, 0, len(level.Benefits))
|
||
for _, benefit := range level.Benefits {
|
||
benefits = append(benefits, benefit.toProto())
|
||
}
|
||
input = append(input, &walletv1.AdminVipLevelInput{
|
||
Level: level.Level,
|
||
Name: strings.TrimSpace(level.Name),
|
||
Status: strings.TrimSpace(level.Status),
|
||
PriceCoin: level.PriceCoin,
|
||
DurationMs: level.DurationMS,
|
||
RewardResourceGroupId: level.RewardResourceGroupID,
|
||
// VIP 累充门槛已下线;后台旧请求即使携带旧字段,也统一写 0,避免旧页面或缓存把门槛重新保存回来。
|
||
RequiredRechargeCoinAmount: 0,
|
||
SortOrder: level.SortOrder,
|
||
Benefits: benefits,
|
||
})
|
||
}
|
||
resp, err := h.wallet.UpdateAdminVipLevels(c.Request.Context(), &walletv1.UpdateAdminVipLevelsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
Levels: input,
|
||
OperatorUserId: int64(middleware.CurrentUserID(c)),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
shared.OperationLogWithResourceID(c, h.audit, "update-vip-config", "vip_levels", appctx.FromContext(c.Request.Context()), "success", strconv.Itoa(len(resp.GetLevels())))
|
||
response.OK(c, configDTO{
|
||
Levels: vipLevelsFromProto(resp.GetLevels()),
|
||
ProgramConfig: vipProgramConfigFromProto(resp.GetProgramConfig()),
|
||
ServerTimeMS: resp.GetServerTimeMs(),
|
||
})
|
||
}
|
||
|
||
func vipLevelsFromProto(items []*walletv1.VipLevel) []vipLevelDTO {
|
||
levels := make([]vipLevelDTO, 0, len(items))
|
||
for _, item := range items {
|
||
if item == nil {
|
||
continue
|
||
}
|
||
levels = append(levels, vipLevelDTO{
|
||
Level: item.GetLevel(),
|
||
Name: item.GetName(),
|
||
Status: item.GetStatus(),
|
||
PriceCoin: item.GetPriceCoin(),
|
||
DurationMS: item.GetDurationMs(),
|
||
RewardResourceGroupID: item.GetRewardResourceGroupId(),
|
||
SortOrder: item.GetSortOrder(),
|
||
// 响应仍保留兼容字段,但后台配置页不再展示或依赖累充资格,统一返回无门槛。
|
||
RechargeGateRequired: false,
|
||
RequiredRechargeCoinAmount: 0,
|
||
UserRechargeCoinAmount: 0,
|
||
PurchaseLockedReason: item.GetPurchaseLockedReason(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
Benefits: vipBenefitsFromProto(item.GetBenefits()),
|
||
ConfigVersion: item.GetConfigVersion(),
|
||
})
|
||
}
|
||
return levels
|
||
}
|
||
|
||
func vipProgramConfigFromProto(item *walletv1.VipProgramConfig) vipProgramConfigDTO {
|
||
if item == nil {
|
||
return vipProgramConfigDTO{}
|
||
}
|
||
return vipProgramConfigDTO{
|
||
AppCode: item.GetAppCode(),
|
||
ProgramType: item.GetProgramType(),
|
||
LevelCount: item.GetLevelCount(),
|
||
SameLevelExpiryPolicy: item.GetSameLevelExpiryPolicy(),
|
||
UpgradeExpiryPolicy: item.GetUpgradeExpiryPolicy(),
|
||
DowngradePurchasePolicy: item.GetDowngradePurchasePolicy(),
|
||
BenefitInheritancePolicy: item.GetBenefitInheritancePolicy(),
|
||
GrantMode: item.GetGrantMode(),
|
||
TrialCardEnabled: item.GetTrialCardEnabled(),
|
||
Status: item.GetStatus(),
|
||
ConfigVersion: item.GetConfigVersion(),
|
||
UpdatedByAdminID: item.GetUpdatedByAdminId(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
}
|
||
}
|
||
|
||
func (item vipProgramConfigDTO) toProto() *walletv1.VipProgramConfig {
|
||
return &walletv1.VipProgramConfig{
|
||
ProgramType: strings.TrimSpace(item.ProgramType),
|
||
LevelCount: item.LevelCount,
|
||
SameLevelExpiryPolicy: strings.TrimSpace(item.SameLevelExpiryPolicy),
|
||
UpgradeExpiryPolicy: strings.TrimSpace(item.UpgradeExpiryPolicy),
|
||
DowngradePurchasePolicy: strings.TrimSpace(item.DowngradePurchasePolicy),
|
||
BenefitInheritancePolicy: strings.TrimSpace(item.BenefitInheritancePolicy),
|
||
GrantMode: strings.TrimSpace(item.GrantMode),
|
||
TrialCardEnabled: item.TrialCardEnabled,
|
||
Status: strings.TrimSpace(item.Status),
|
||
}
|
||
}
|
||
|
||
func vipBenefitsFromProto(items []*walletv1.VipBenefit) []vipBenefitDTO {
|
||
benefits := make([]vipBenefitDTO, 0, len(items))
|
||
for _, item := range items {
|
||
if item == nil {
|
||
continue
|
||
}
|
||
benefits = append(benefits, vipBenefitDTO{
|
||
BenefitCode: item.GetBenefitCode(),
|
||
Name: item.GetName(),
|
||
BenefitType: item.GetBenefitType(),
|
||
UnlockLevel: item.GetUnlockLevel(),
|
||
Status: item.GetStatus(),
|
||
TrialEnabled: item.GetTrialEnabled(),
|
||
ResourceID: item.GetResourceId(),
|
||
ResourceType: item.GetResourceType(),
|
||
ExecutionScope: item.GetExecutionScope(),
|
||
AutoEquip: item.GetAutoEquip(),
|
||
SortOrder: item.GetSortOrder(),
|
||
MetadataJSON: item.GetMetadataJson(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
})
|
||
}
|
||
return benefits
|
||
}
|
||
|
||
func (item vipBenefitDTO) toProto() *walletv1.VipBenefit {
|
||
return &walletv1.VipBenefit{
|
||
BenefitCode: strings.TrimSpace(item.BenefitCode),
|
||
Name: strings.TrimSpace(item.Name),
|
||
BenefitType: strings.TrimSpace(item.BenefitType),
|
||
UnlockLevel: item.UnlockLevel,
|
||
Status: strings.TrimSpace(item.Status),
|
||
TrialEnabled: item.TrialEnabled,
|
||
ResourceId: item.ResourceID,
|
||
ResourceType: strings.TrimSpace(item.ResourceType),
|
||
ExecutionScope: strings.TrimSpace(item.ExecutionScope),
|
||
AutoEquip: item.AutoEquip,
|
||
SortOrder: item.SortOrder,
|
||
MetadataJson: strings.TrimSpace(item.MetadataJSON),
|
||
}
|
||
}
|
||
|
||
func grantVipFromProto(resp *walletv1.GrantVipResponse) grantVipDTO {
|
||
if resp == nil {
|
||
return grantVipDTO{}
|
||
}
|
||
return grantVipDTO{
|
||
TransactionID: resp.GetTransactionId(),
|
||
VIP: userVipFromProto(resp.GetVip()),
|
||
RewardItems: vipRewardItemsFromProto(resp.GetRewardItems()),
|
||
ServerTimeMS: resp.GetServerTimeMs(),
|
||
}
|
||
}
|
||
|
||
func userVipFromProto(item *walletv1.UserVip) userVipDTO {
|
||
if item == nil {
|
||
return userVipDTO{}
|
||
}
|
||
return userVipDTO{
|
||
UserID: strconv.FormatInt(item.GetUserId(), 10),
|
||
Level: item.GetLevel(),
|
||
Name: item.GetName(),
|
||
Active: item.GetActive(),
|
||
StartedAtMS: item.GetStartedAtMs(),
|
||
ExpiresAtMS: item.GetExpiresAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
ProgramType: item.GetProgramType(),
|
||
ConfigVersion: item.GetConfigVersion(),
|
||
}
|
||
}
|
||
|
||
func grantVipTrialCardFromProto(resp *walletv1.GrantVipTrialCardResponse) grantVipTrialCardDTO {
|
||
if resp == nil {
|
||
return grantVipTrialCardDTO{}
|
||
}
|
||
return grantVipTrialCardDTO{
|
||
TrialCard: vipTrialCardFromProto(resp.GetTrialCard()),
|
||
State: vipStateFromProto(resp.GetState()),
|
||
ServerTimeMS: resp.GetServerTimeMs(),
|
||
}
|
||
}
|
||
|
||
func vipTrialCardFromProto(item *walletv1.VipTrialCard) vipTrialCardDTO {
|
||
if item == nil {
|
||
return vipTrialCardDTO{}
|
||
}
|
||
return vipTrialCardDTO{
|
||
TrialCardID: item.GetTrialCardId(),
|
||
EntitlementID: item.GetEntitlementId(),
|
||
ResourceID: item.GetResourceId(),
|
||
UserID: strconv.FormatInt(item.GetUserId(), 10),
|
||
Level: item.GetLevel(),
|
||
Name: item.GetName(),
|
||
Status: item.GetStatus(),
|
||
Equipped: item.GetEquipped(),
|
||
DurationMS: item.GetDurationMs(),
|
||
EffectiveAtMS: item.GetEffectiveAtMs(),
|
||
ExpiresAtMS: item.GetExpiresAtMs(),
|
||
RemainingDurationMS: item.GetRemainingDurationMs(),
|
||
GrantSource: item.GetGrantSource(),
|
||
SourceGrantID: item.GetSourceGrantId(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
}
|
||
}
|
||
|
||
func vipStateFromProto(item *walletv1.VipState) vipStateDTO {
|
||
if item == nil {
|
||
return vipStateDTO{EffectiveBenefits: []vipBenefitDTO{}}
|
||
}
|
||
return vipStateDTO{
|
||
PaidVIP: userVipFromProto(item.GetPaidVip()),
|
||
EquippedTrialCard: vipTrialCardFromProto(item.GetEquippedTrialCard()),
|
||
EffectiveVIP: userVipFromProto(item.GetEffectiveVip()),
|
||
EffectiveSource: item.GetEffectiveSource(),
|
||
EffectiveBenefits: vipBenefitsFromProto(item.GetEffectiveBenefits()),
|
||
EvaluatedAtMS: item.GetEvaluatedAtMs(),
|
||
ProgramConfig: vipProgramConfigFromProto(item.GetProgramConfig()),
|
||
}
|
||
}
|
||
|
||
func vipRewardItemsFromProto(items []*walletv1.VipRewardItem) []vipRewardItemDTO {
|
||
rewards := make([]vipRewardItemDTO, 0, len(items))
|
||
for _, item := range items {
|
||
if item == nil {
|
||
continue
|
||
}
|
||
rewards = append(rewards, vipRewardItemDTO{
|
||
ResourceID: strconv.FormatInt(item.GetResourceId(), 10),
|
||
ResourceCode: item.GetResourceCode(),
|
||
ResourceType: item.GetResourceType(),
|
||
Name: item.GetName(),
|
||
Quantity: item.GetQuantity(),
|
||
ExpiresAtMS: item.GetExpiresAtMs(),
|
||
})
|
||
}
|
||
return rewards
|
||
}
|
||
|
||
func parseFlexibleUserID(value any) (int64, bool) {
|
||
switch typed := value.(type) {
|
||
case float64:
|
||
userID := int64(typed)
|
||
return userID, userID > 0 && float64(userID) == typed
|
||
case string:
|
||
userID, err := strconv.ParseInt(strings.TrimSpace(typed), 10, 64)
|
||
return userID, err == nil && userID > 0
|
||
default:
|
||
return 0, false
|
||
}
|
||
}
|