2026-07-17 16:55:22 +08:00

510 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}