510 lines
24 KiB
Go
510 lines
24 KiB
Go
package wallet
|
||
|
||
import (
|
||
"context"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
"hyapp/services/wallet-service/internal/domain/ledger"
|
||
resourcedomain "hyapp/services/wallet-service/internal/domain/resource"
|
||
)
|
||
|
||
func (s *Service) ListResources(ctx context.Context, query resourcedomain.ListResourcesQuery) ([]resourcedomain.Resource, int64, error) {
|
||
if s.repository == nil {
|
||
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
if strings.TrimSpace(query.ResourceType) != "" && !resourcedomain.ValidResourceType(query.ResourceType) {
|
||
return nil, 0, xerr.New(xerr.InvalidArgument, "resource_type is invalid")
|
||
}
|
||
return s.repository.ListResources(ctx, query)
|
||
}
|
||
|
||
func (s *Service) GetResource(ctx context.Context, appCode string, resourceID int64) (resourcedomain.Resource, error) {
|
||
if resourceID <= 0 {
|
||
return resourcedomain.Resource{}, xerr.New(xerr.InvalidArgument, "resource_id is required")
|
||
}
|
||
if s.repository == nil {
|
||
return resourcedomain.Resource{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
ctx = appcode.WithContext(ctx, appcode.Normalize(appCode))
|
||
return s.repository.GetResource(ctx, resourceID)
|
||
}
|
||
|
||
func (s *Service) CreateResource(ctx context.Context, command resourcedomain.ResourceCommand) (resourcedomain.Resource, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.Resource{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.CreateResource(ctx, command)
|
||
}
|
||
|
||
func (s *Service) UpdateResource(ctx context.Context, command resourcedomain.ResourceCommand) (resourcedomain.Resource, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.Resource{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.UpdateResource(ctx, command)
|
||
}
|
||
|
||
func (s *Service) SetResourceStatus(ctx context.Context, command resourcedomain.StatusCommand) (resourcedomain.Resource, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.Resource{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.SetResourceStatus(ctx, command)
|
||
}
|
||
|
||
func (s *Service) DeleteResource(ctx context.Context, command resourcedomain.StatusCommand) (resourcedomain.Resource, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.Resource{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.DeleteResource(ctx, command)
|
||
}
|
||
|
||
func (s *Service) BatchDeleteResources(ctx context.Context, command resourcedomain.BatchDeleteResourcesCommand) ([]resourcedomain.Resource, error) {
|
||
if s.repository == nil {
|
||
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.BatchDeleteResources(ctx, command)
|
||
}
|
||
|
||
func (s *Service) ListResourceGroups(ctx context.Context, query resourcedomain.ListResourceGroupsQuery) ([]resourcedomain.ResourceGroup, int64, error) {
|
||
if s.repository == nil {
|
||
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
return s.repository.ListResourceGroups(ctx, query)
|
||
}
|
||
|
||
func (s *Service) GetResourceGroup(ctx context.Context, appCode string, groupID int64) (resourcedomain.ResourceGroup, error) {
|
||
if groupID <= 0 {
|
||
return resourcedomain.ResourceGroup{}, xerr.New(xerr.InvalidArgument, "group_id is required")
|
||
}
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGroup{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
ctx = appcode.WithContext(ctx, appcode.Normalize(appCode))
|
||
return s.repository.GetResourceGroup(ctx, groupID)
|
||
}
|
||
|
||
func (s *Service) CreateResourceGroup(ctx context.Context, command resourcedomain.ResourceGroupCommand) (resourcedomain.ResourceGroup, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGroup{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.CreateResourceGroup(ctx, command)
|
||
}
|
||
|
||
func (s *Service) UpdateResourceGroup(ctx context.Context, command resourcedomain.ResourceGroupCommand) (resourcedomain.ResourceGroup, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGroup{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.UpdateResourceGroup(ctx, command)
|
||
}
|
||
|
||
func (s *Service) SetResourceGroupStatus(ctx context.Context, command resourcedomain.StatusCommand) (resourcedomain.ResourceGroup, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGroup{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.SetResourceGroupStatus(ctx, command)
|
||
}
|
||
|
||
func (s *Service) PinResourceGroupSnapshot(ctx context.Context, command resourcedomain.PinResourceGroupSnapshotCommand) (resourcedomain.ResourceGroupSnapshot, error) {
|
||
store, ok := s.repository.(interface {
|
||
PinResourceGroupSnapshot(context.Context, resourcedomain.PinResourceGroupSnapshotCommand) (resourcedomain.ResourceGroupSnapshot, error)
|
||
})
|
||
if !ok {
|
||
return resourcedomain.ResourceGroupSnapshot{}, xerr.New(xerr.Unavailable, "resource group snapshot repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return store.PinResourceGroupSnapshot(ctx, command)
|
||
}
|
||
|
||
func (s *Service) ListGiftConfigs(ctx context.Context, query resourcedomain.ListGiftConfigsQuery) ([]resourcedomain.GiftConfig, int64, error) {
|
||
if s.repository == nil {
|
||
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
if query.FilterRegion && query.RegionID < 0 {
|
||
return nil, 0, xerr.New(xerr.InvalidArgument, "region_id is invalid")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
return s.repository.ListGiftConfigs(ctx, query)
|
||
}
|
||
|
||
func (s *Service) ListGiftTypeConfigs(ctx context.Context, query resourcedomain.ListGiftTypeConfigsQuery) ([]resourcedomain.GiftTypeConfig, error) {
|
||
if s.repository == nil {
|
||
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
return s.repository.ListGiftTypeConfigs(ctx, query)
|
||
}
|
||
|
||
func (s *Service) GetGiftCatalogVersion(ctx context.Context, appCode string) (int64, error) {
|
||
if s.repository == nil {
|
||
return 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
appCode = appcode.Normalize(appCode)
|
||
ctx = appcode.WithContext(ctx, appCode)
|
||
return s.repository.GetGiftCatalogVersion(ctx, appCode)
|
||
}
|
||
|
||
func (s *Service) CreateGiftConfig(ctx context.Context, command resourcedomain.GiftConfigCommand) (resourcedomain.GiftConfig, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.GiftConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.CreateGiftConfig(ctx, command)
|
||
}
|
||
|
||
func (s *Service) BatchCreateGiftConfigs(ctx context.Context, appCode string, commands []resourcedomain.GiftConfigCommand, operatorUserID int64) ([]resourcedomain.GiftConfig, error) {
|
||
if s.repository == nil {
|
||
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
appCode = appcode.Normalize(appCode)
|
||
ctx = appcode.WithContext(ctx, appCode)
|
||
if len(commands) == 0 {
|
||
return nil, xerr.New(xerr.InvalidArgument, "gift configs are required")
|
||
}
|
||
batchCommands := make([]resourcedomain.GiftConfigCommand, 0, len(commands))
|
||
for _, command := range commands {
|
||
command.AppCode = appCode
|
||
if command.OperatorUserID == 0 {
|
||
command.OperatorUserID = operatorUserID
|
||
}
|
||
batchCommands = append(batchCommands, command)
|
||
}
|
||
return s.repository.BatchCreateGiftConfigs(ctx, batchCommands)
|
||
}
|
||
|
||
func (s *Service) UpdateGiftConfig(ctx context.Context, command resourcedomain.GiftConfigCommand) (resourcedomain.GiftConfig, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.GiftConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.UpdateGiftConfig(ctx, command)
|
||
}
|
||
|
||
func (s *Service) SetGiftConfigStatus(ctx context.Context, command resourcedomain.StatusCommand) (resourcedomain.GiftConfig, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.GiftConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.SetGiftConfigStatus(ctx, command)
|
||
}
|
||
|
||
func (s *Service) DeleteGiftConfig(ctx context.Context, command resourcedomain.StatusCommand) (resourcedomain.GiftConfig, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.GiftConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.DeleteGiftConfig(ctx, command)
|
||
}
|
||
|
||
func (s *Service) UpsertGiftTypeConfig(ctx context.Context, command resourcedomain.GiftTypeConfigCommand) (resourcedomain.GiftTypeConfig, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.GiftTypeConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.UpsertGiftTypeConfig(ctx, command)
|
||
}
|
||
|
||
func (s *Service) GrantResource(ctx context.Context, command resourcedomain.GrantResourceCommand) (resourcedomain.ResourceGrant, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGrant{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.GrantResource(ctx, command)
|
||
}
|
||
|
||
func (s *Service) GrantResourceGroup(ctx context.Context, command resourcedomain.GrantResourceGroupCommand) (resourcedomain.ResourceGrant, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGrant{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.GrantResourceGroup(ctx, command)
|
||
}
|
||
|
||
func (s *Service) GrantPinnedResourceGroup(ctx context.Context, command resourcedomain.GrantPinnedResourceGroupCommand) (resourcedomain.ResourceGrant, error) {
|
||
store, ok := s.repository.(interface {
|
||
GrantPinnedResourceGroup(context.Context, resourcedomain.GrantPinnedResourceGroupCommand) (resourcedomain.ResourceGrant, error)
|
||
})
|
||
if !ok {
|
||
return resourcedomain.ResourceGrant{}, xerr.New(xerr.Unavailable, "pinned resource group grant repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return store.GrantPinnedResourceGroup(ctx, command)
|
||
}
|
||
|
||
func (s *Service) RevokeResourceGrant(ctx context.Context, command resourcedomain.RevokeResourceGrantCommand) (resourcedomain.ResourceGrant, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceGrant{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.RevokeResourceGrant(ctx, command)
|
||
}
|
||
|
||
func (s *Service) ListUserResources(ctx context.Context, query resourcedomain.ListUserResourcesQuery) ([]resourcedomain.UserResourceEntitlement, error) {
|
||
if s.repository == nil {
|
||
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
return s.repository.ListUserResources(ctx, query)
|
||
}
|
||
|
||
func (s *Service) RevokeUserResource(ctx context.Context, command resourcedomain.RevokeUserResourceCommand) (resourcedomain.UserResourceEntitlement, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.RevokeUserResource(ctx, command)
|
||
}
|
||
|
||
func (s *Service) EquipUserResource(ctx context.Context, command resourcedomain.EquipUserResourceCommand) (resourcedomain.UserResourceEntitlement, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
if command.UserID <= 0 || command.ResourceID <= 0 {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.InvalidArgument, "user_id and resource_id are required")
|
||
}
|
||
command.RequestID = strings.TrimSpace(command.RequestID)
|
||
command.CommandID = strings.TrimSpace(command.CommandID)
|
||
if command.CommandID == "" {
|
||
// 兼容旧内部调用方:历史代码只传 request_id。新 HTTP 契约会显式传 command_id;
|
||
// 这里的回退只防止 Lalu/既有任务立即失效,不把 request_id 宣称为新的业务协议。
|
||
command.CommandID = command.RequestID
|
||
}
|
||
if len(command.CommandID) > 128 {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.InvalidArgument, "command_id is too long")
|
||
}
|
||
command.EntitlementID = strings.TrimSpace(command.EntitlementID)
|
||
if command.CommandID != "" {
|
||
// 完成命令必须在当前 entitlement/VIP 状态校验前重放;否则首请求成功后资源到期,
|
||
// 同 command_id 会错误地变成 NotFound/VIP_BENEFIT_REQUIRED,破坏幂等回包契约。
|
||
if replayed, exists, err := s.repository.ReplayEquipUserResource(ctx, command); err != nil || exists {
|
||
return replayed, err
|
||
}
|
||
}
|
||
items, err := s.repository.ListUserResources(ctx, resourcedomain.ListUserResourcesQuery{
|
||
AppCode: command.AppCode, UserID: command.UserID, ActiveOnly: true,
|
||
})
|
||
if err != nil {
|
||
return resourcedomain.UserResourceEntitlement{}, err
|
||
}
|
||
var selected *resourcedomain.UserResourceEntitlement
|
||
for index := range items {
|
||
if items[index].ResourceID == command.ResourceID && (command.EntitlementID == "" || items[index].EntitlementID == command.EntitlementID) {
|
||
selected = &items[index]
|
||
break
|
||
}
|
||
}
|
||
if selected == nil {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.NotFound, "user resource entitlement not found")
|
||
}
|
||
if err := s.requireVipBenefitForResourceEquip(ctx, command.UserID, command.ResourceID, selected.Resource.ResourceType); err != nil {
|
||
return resourcedomain.UserResourceEntitlement{}, err
|
||
}
|
||
if resourcedomain.NormalizeResourceType(selected.Resource.ResourceType) == resourcedomain.TypeVIPTrialCard {
|
||
// 体验卡在 UI 上仍是普通背包物品,但其装备动作会改变最终 VIP 权益。
|
||
// 因此通用入口必须委托专用状态机,不能直接写 equipment 而绕过 App 开关和 VipEffectiveChanged outbox。
|
||
command.EntitlementID = strings.TrimSpace(command.EntitlementID)
|
||
if command.CommandID == "" || command.EntitlementID == "" {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.InvalidArgument, "command_id and entitlement_id are required for vip_trial_card")
|
||
}
|
||
if selected.EntitlementID != command.EntitlementID {
|
||
return resourcedomain.UserResourceEntitlement{}, xerr.New(xerr.NotFound, "vip trial card entitlement not found")
|
||
}
|
||
if _, _, err := s.EquipVipTrialCard(ctx, ledger.EquipVipTrialCardCommand{
|
||
AppCode: command.AppCode,
|
||
// 体验卡专用状态机的 RequestID 实际承担稳定 outbox event key;这里必须使用业务 command_id,
|
||
// 不能退回每次重试都会变化的 HTTP request_id。
|
||
RequestID: command.CommandID,
|
||
EquipmentCommandID: command.CommandID,
|
||
ResourceID: command.ResourceID,
|
||
UserID: command.UserID,
|
||
EntitlementID: command.EntitlementID,
|
||
}); err != nil {
|
||
return resourcedomain.UserResourceEntitlement{}, err
|
||
}
|
||
// 专用事务已经再次校验 entitlement 并提交装备/outbox;这里复用预读完整资源投影满足通用 API 返回契约。
|
||
selected.Equipped = true
|
||
return *selected, nil
|
||
}
|
||
return s.repository.EquipUserResource(ctx, command)
|
||
}
|
||
|
||
// requireVipBenefitForResourceEquip 对 tiered VIP 绑定资源执行服务端二次门禁。
|
||
// repository 的绑定查询已联结 program_type,因此 Lalu legacy_timed 资源不会进入此分支;
|
||
// Fami 用户即使持有历史 entitlement,也必须仍有对应 effective benefit 才能重新装备。
|
||
func (s *Service) requireVipBenefitForResourceEquip(ctx context.Context, userID int64, resourceID int64, resourceType string) error {
|
||
requirements, err := s.repository.ListVipBenefitRequirementsForResource(ctx, resourceID)
|
||
if err != nil || len(requirements) == 0 {
|
||
return err
|
||
}
|
||
var denied ledger.CheckVipBenefitResult
|
||
for _, requirement := range requirements {
|
||
result, checkErr := s.repository.CheckVipBenefit(ctx, userID, requirement.BenefitCode)
|
||
if checkErr != nil {
|
||
// 权益 owner 读取失败必须失败关闭,不能因为用户已有 entitlement 就跳过付费门禁。
|
||
return checkErr
|
||
}
|
||
// 权益 code 相同仍不代表样式相同:VIP8 的旧 entitlement 在切到 VIP4 后可能尚未到期。
|
||
// 只有 wallet 当前 effective benefit 精确指向本 resource_id/type 才允许重新装备。
|
||
if result.Allowed && result.Benefit.ResourceID == resourceID &&
|
||
resourcedomain.NormalizeResourceType(result.Benefit.ResourceType) == resourcedomain.NormalizeResourceType(resourceType) &&
|
||
resourcedomain.NormalizeResourceType(requirement.ResourceType) == resourcedomain.NormalizeResourceType(resourceType) {
|
||
return nil
|
||
}
|
||
denied = result
|
||
}
|
||
requirement := requirements[0]
|
||
return xerr.NewWithMetadata(xerr.VIPBenefitRequired, "vip benefit is required for resource equip", map[string]string{
|
||
"benefit_code": requirement.BenefitCode,
|
||
"required_level": strconv.FormatInt(int64(denied.RequiredLevel), 10),
|
||
"current_effective_level": strconv.FormatInt(int64(denied.CurrentEffectiveLevel), 10),
|
||
"action": "equip_resource",
|
||
})
|
||
}
|
||
|
||
func (s *Service) UnequipUserResource(ctx context.Context, command resourcedomain.UnequipUserResourceCommand) (resourcedomain.UnequipUserResourceResult, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.UnequipUserResourceResult{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
command.ResourceType = resourcedomain.NormalizeResourceType(command.ResourceType)
|
||
if command.ResourceType == resourcedomain.TypeVIPTrialCard {
|
||
// 和装备相同,通用卸下必须发布 VIP 有效态变化,不能只删除通用装备指针。
|
||
unequipped, state, err := s.UnequipVipTrialCard(ctx, ledger.UnequipVipTrialCardCommand{
|
||
AppCode: command.AppCode,
|
||
RequestID: strings.TrimSpace(command.RequestID),
|
||
UserID: command.UserID,
|
||
})
|
||
if err != nil {
|
||
return resourcedomain.UnequipUserResourceResult{}, err
|
||
}
|
||
return resourcedomain.UnequipUserResourceResult{
|
||
ResourceType: command.ResourceType,
|
||
Unequipped: unequipped,
|
||
UpdatedAtMS: state.EvaluatedAtMS,
|
||
}, nil
|
||
}
|
||
return s.repository.UnequipUserResource(ctx, command)
|
||
}
|
||
|
||
func (s *Service) BatchGetUserEquippedResources(ctx context.Context, query resourcedomain.BatchGetUserEquippedResourcesQuery) ([]resourcedomain.UserEquippedResources, error) {
|
||
if s.repository == nil {
|
||
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
return s.repository.BatchGetUserEquippedResources(ctx, query)
|
||
}
|
||
|
||
func (s *Service) ListResourceGrants(ctx context.Context, query resourcedomain.ListResourceGrantsQuery) ([]resourcedomain.ResourceGrant, int64, error) {
|
||
if s.repository == nil {
|
||
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
return s.repository.ListResourceGrants(ctx, query)
|
||
}
|
||
|
||
func (s *Service) ListResourceShopItems(ctx context.Context, query resourcedomain.ListResourceShopItemsQuery) ([]resourcedomain.ResourceShopItem, int64, error) {
|
||
if s.repository == nil {
|
||
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
if strings.TrimSpace(query.ResourceType) != "" {
|
||
resourceType := resourcedomain.NormalizeResourceType(query.ResourceType)
|
||
if !resourcedomain.ResourceTypeSellableInShop(resourceType) {
|
||
return nil, 0, xerr.New(xerr.InvalidArgument, "resource_type is invalid")
|
||
}
|
||
query.ResourceType = resourceType
|
||
}
|
||
return s.repository.ListResourceShopItems(ctx, query)
|
||
}
|
||
|
||
func (s *Service) UpsertResourceShopItems(ctx context.Context, command resourcedomain.ResourceShopItemsCommand) ([]resourcedomain.ResourceShopItem, error) {
|
||
if s.repository == nil {
|
||
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.UpsertResourceShopItems(ctx, command)
|
||
}
|
||
|
||
func (s *Service) SetResourceShopItemStatus(ctx context.Context, command resourcedomain.ResourceShopItemStatusCommand) (resourcedomain.ResourceShopItem, error) {
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceShopItem{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.SetResourceShopItemStatus(ctx, command)
|
||
}
|
||
|
||
func (s *Service) ListResourceShopPurchaseOrders(ctx context.Context, query resourcedomain.ListResourceShopPurchaseOrdersQuery) ([]resourcedomain.ResourceShopPurchaseOrder, int64, error) {
|
||
if s.repository == nil {
|
||
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
query.AppCode = appcode.Normalize(query.AppCode)
|
||
ctx = appcode.WithContext(ctx, query.AppCode)
|
||
if strings.TrimSpace(query.ResourceType) != "" {
|
||
resourceType := resourcedomain.NormalizeResourceType(query.ResourceType)
|
||
if !resourcedomain.ResourceTypeSellableInShop(resourceType) {
|
||
return nil, 0, xerr.New(xerr.InvalidArgument, "resource_type is invalid")
|
||
}
|
||
query.ResourceType = resourceType
|
||
}
|
||
return s.repository.ListResourceShopPurchaseOrders(ctx, query)
|
||
}
|
||
|
||
func (s *Service) PurchaseResourceShopItem(ctx context.Context, command resourcedomain.ResourceShopPurchaseCommand) (resourcedomain.ResourceShopPurchaseReceipt, error) {
|
||
if strings.TrimSpace(command.CommandID) == "" || command.UserID <= 0 || command.ShopItemID <= 0 {
|
||
return resourcedomain.ResourceShopPurchaseReceipt{}, xerr.New(xerr.InvalidArgument, "resource shop purchase command is incomplete")
|
||
}
|
||
if len(command.CommandID) > 128 {
|
||
return resourcedomain.ResourceShopPurchaseReceipt{}, xerr.New(xerr.InvalidArgument, "command_id is too long")
|
||
}
|
||
if s.repository == nil {
|
||
return resourcedomain.ResourceShopPurchaseReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.CommandID = strings.TrimSpace(command.CommandID)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.PurchaseResourceShopItem(ctx, command)
|
||
}
|