2026-07-23 19:51:18 +08:00

534 lines
25 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/idgen"
"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)
// 资源编码是系统业务键,不再要求后台运营手工维护;仅在创建请求未提供编码时生成,
// 以兼容批量导入、初始化脚本等仍需指定稳定编码的内部调用方。
if strings.TrimSpace(command.ResourceCode) == "" {
command.ResourceCode = idgen.New("resource")
}
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)
// 资源组编码由 wallet owner 生成,随机熵与数据库唯一键共同保证并发创建安全;
// 显式编码仍保留给初始化和跨租户映射流程使用。
if strings.TrimSpace(command.GroupCode) == "" {
command.GroupCode = idgen.New("resource_group")
}
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 资源不会进入此分支;
// 资源型权益允许当前 effective 等级向下继承,但历史 entitlement 仍不能绕过当前 active 目录绑定。
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.CheckVipResourceBenefit(ctx, userID, requirement.BenefitCode, resourceID, resourceType)
if checkErr != nil {
// 权益 owner 读取失败必须失败关闭,不能因为用户已有 entitlement 就跳过付费门禁。
return checkErr
}
// resource_id/type 已由 owner 查询限定;这里再次核对返回投影,避免未来仓储实现
// 只按 benefit_code 放行而把同类其他等级素材错误装备。
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)
}
// GetResourceGrant 只按当前 app_code 读取一个精确 grant避免异步消费者分页扫描后误拿同用户的其它活动奖励。
func (s *Service) GetResourceGrant(ctx context.Context, appCode string, grantID string) (resourcedomain.ResourceGrant, error) {
if s.repository == nil {
return resourcedomain.ResourceGrant{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
appCode = appcode.Normalize(appCode)
grantID = strings.TrimSpace(grantID)
if grantID == "" {
return resourcedomain.ResourceGrant{}, xerr.New(xerr.InvalidArgument, "grant_id is required")
}
return s.repository.GetResourceGrant(appcode.WithContext(ctx, appCode), grantID)
}
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)
}