555 lines
19 KiB
Go
555 lines
19 KiB
Go
package resourceapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"hyapp/services/gateway-service/internal/transport/http/httpkit"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
walletv1 "hyapp.local/api/proto/wallet/v1"
|
|
"hyapp/pkg/appcode"
|
|
"hyapp/services/gateway-service/internal/auth"
|
|
)
|
|
|
|
const resourceTypeEmojiPack = "emoji_pack"
|
|
|
|
const (
|
|
defaultEmojiPackCategory = "默认"
|
|
emojiPackPricingFree = "free"
|
|
emojiPackPricingPaid = "paid"
|
|
)
|
|
|
|
type resourceData struct {
|
|
ResourceID int64 `json:"resource_id"`
|
|
ResourceCode string `json:"resource_code"`
|
|
ResourceType string `json:"resource_type"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
Grantable bool `json:"grantable"`
|
|
GrantStrategy string `json:"grant_strategy"`
|
|
WalletAssetType string `json:"wallet_asset_type,omitempty"`
|
|
WalletAssetAmount int64 `json:"wallet_asset_amount,omitempty"`
|
|
UsageScopes []string `json:"usage_scopes"`
|
|
AssetURL string `json:"asset_url"`
|
|
PreviewURL string `json:"preview_url"`
|
|
AnimationURL string `json:"animation_url"`
|
|
MetadataJSON string `json:"metadata_json"`
|
|
SortOrder int32 `json:"sort_order"`
|
|
CreatedAtMS int64 `json:"created_at_ms"`
|
|
UpdatedAtMS int64 `json:"updated_at_ms"`
|
|
}
|
|
|
|
type resourceGroupItemData struct {
|
|
GroupItemID int64 `json:"group_item_id"`
|
|
ItemType string `json:"item_type"`
|
|
ResourceID int64 `json:"resource_id"`
|
|
Resource *resourceData `json:"resource,omitempty"`
|
|
WalletAssetType string `json:"wallet_asset_type,omitempty"`
|
|
WalletAssetAmount int64 `json:"wallet_asset_amount,omitempty"`
|
|
Quantity int64 `json:"quantity"`
|
|
DurationMS int64 `json:"duration_ms"`
|
|
SortOrder int32 `json:"sort_order"`
|
|
}
|
|
|
|
type resourceGroupData struct {
|
|
GroupID int64 `json:"group_id"`
|
|
GroupCode string `json:"group_code"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
Description string `json:"description"`
|
|
SortOrder int32 `json:"sort_order"`
|
|
Items []resourceGroupItemData `json:"items"`
|
|
CreatedAtMS int64 `json:"created_at_ms"`
|
|
UpdatedAtMS int64 `json:"updated_at_ms"`
|
|
}
|
|
|
|
type giftConfigData struct {
|
|
GiftID string `json:"gift_id"`
|
|
ResourceID int64 `json:"resource_id"`
|
|
Resource resourceData `json:"resource"`
|
|
Status string `json:"status"`
|
|
Name string `json:"name"`
|
|
SortOrder int32 `json:"sort_order"`
|
|
PresentationJSON string `json:"presentation_json"`
|
|
PriceVersion string `json:"price_version"`
|
|
GiftTypeCode string `json:"gift_type_code"`
|
|
ChargeAssetType string `json:"charge_asset_type"`
|
|
CoinPrice int64 `json:"coin_price"`
|
|
GiftPointAmount int64 `json:"gift_point_amount"`
|
|
HeatValue int64 `json:"heat_value"`
|
|
EffectiveFromMS int64 `json:"effective_from_ms"`
|
|
EffectiveToMS int64 `json:"effective_to_ms"`
|
|
EffectTypes []string `json:"effect_types"`
|
|
CreatedAtMS int64 `json:"created_at_ms"`
|
|
UpdatedAtMS int64 `json:"updated_at_ms"`
|
|
RegionIDs []int64 `json:"region_ids"`
|
|
}
|
|
|
|
type emojiPackData struct {
|
|
ResourceID int64 `json:"resource_id"`
|
|
ResourceCode string `json:"resource_code"`
|
|
Name string `json:"name"`
|
|
Category string `json:"category"`
|
|
PricingType string `json:"pricing_type"`
|
|
CoverURL string `json:"cover_url"`
|
|
AnimationURL string `json:"animation_url"`
|
|
RegionIDs []int64 `json:"region_ids"`
|
|
IsGlobal bool `json:"is_global"`
|
|
SortOrder int32 `json:"sort_order"`
|
|
UpdatedAtMS int64 `json:"updated_at_ms"`
|
|
}
|
|
|
|
type emojiPackMetadata struct {
|
|
Category string `json:"category"`
|
|
PricingType string `json:"pricing_type"`
|
|
RegionIDs []int64 `json:"region_ids"`
|
|
}
|
|
|
|
type userResourceData struct {
|
|
EntitlementID string `json:"entitlement_id"`
|
|
ResourceID int64 `json:"resource_id"`
|
|
Resource resourceData `json:"resource"`
|
|
Status string `json:"status"`
|
|
Quantity int64 `json:"quantity"`
|
|
RemainingQuantity int64 `json:"remaining_quantity"`
|
|
EffectiveAtMS int64 `json:"effective_at_ms"`
|
|
ExpiresAtMS int64 `json:"expires_at_ms"`
|
|
SourceGrantID string `json:"source_grant_id"`
|
|
CreatedAtMS int64 `json:"created_at_ms"`
|
|
UpdatedAtMS int64 `json:"updated_at_ms"`
|
|
}
|
|
|
|
func (h *Handler) listResources(writer http.ResponseWriter, request *http.Request) {
|
|
if h.walletClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
page, pageSize, ok := resourcePage(request)
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
resp, err := h.walletClient.ListResources(request.Context(), &walletv1.ListResourcesRequest{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
ResourceType: strings.TrimSpace(request.URL.Query().Get("resource_type")),
|
|
Keyword: strings.TrimSpace(request.URL.Query().Get("keyword")),
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
ActiveOnly: true,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
items := make([]resourceData, 0, len(resp.GetResources()))
|
|
for _, item := range resp.GetResources() {
|
|
items = append(items, resourceFromProto(item))
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": resp.GetTotal(), "page": page, "page_size": pageSize})
|
|
}
|
|
|
|
func (h *Handler) getResourceGroup(writer http.ResponseWriter, request *http.Request) {
|
|
if h.walletClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
groupID, ok := httpkit.PositiveInt64PathValue(request, "group_id")
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
resp, err := h.walletClient.GetResourceGroup(request.Context(), &walletv1.GetResourceGroupRequest{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
GroupId: groupID,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
group := resourceGroupFromProto(resp.GetGroup())
|
|
if group.Status != "active" {
|
|
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
|
|
return
|
|
}
|
|
httpkit.WriteOK(writer, request, group)
|
|
}
|
|
|
|
func (h *Handler) listGifts(writer http.ResponseWriter, request *http.Request) {
|
|
if h.walletClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
page, pageSize, ok := resourcePage(request)
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
regionID, _, ok := optionalNonNegativeInt64Query(request, "region_id")
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
resp, err := h.walletClient.ListGiftConfigs(request.Context(), &walletv1.ListGiftConfigsRequest{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
Keyword: strings.TrimSpace(request.URL.Query().Get("keyword")),
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
ActiveOnly: true,
|
|
RegionId: regionID,
|
|
FilterRegion: true,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
items := make([]giftConfigData, 0, len(resp.GetGifts()))
|
|
for _, item := range resp.GetGifts() {
|
|
items = append(items, giftFromProto(item))
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": resp.GetTotal(), "page": page, "page_size": pageSize})
|
|
}
|
|
|
|
func (h *Handler) listEmojiPacks(writer http.ResponseWriter, request *http.Request) {
|
|
if h.walletClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
page, pageSize, ok := resourcePage(request)
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
regionID, filterRegion, ok := optionalNonNegativeInt64Query(request, "region_id")
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
resp, err := h.walletClient.ListResources(request.Context(), &walletv1.ListResourcesRequest{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
ResourceType: resourceTypeEmojiPack,
|
|
Keyword: strings.TrimSpace(request.URL.Query().Get("keyword")),
|
|
Page: 1,
|
|
PageSize: 1000,
|
|
ActiveOnly: true,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
all := make([]emojiPackData, 0, len(resp.GetResources()))
|
|
for _, item := range resp.GetResources() {
|
|
pack := emojiPackFromProto(item)
|
|
if filterRegion && !emojiPackMatchesRegion(pack, regionID) {
|
|
continue
|
|
}
|
|
all = append(all, pack)
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"items": paginateEmojiPacks(all, page, pageSize), "total": int64(len(all)), "page": page, "page_size": pageSize})
|
|
}
|
|
|
|
func (h *Handler) listMyResources(writer http.ResponseWriter, request *http.Request) {
|
|
if h.walletClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
resp, err := h.walletClient.ListUserResources(request.Context(), &walletv1.ListUserResourcesRequest{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
ResourceType: strings.TrimSpace(request.URL.Query().Get("resource_type")),
|
|
ActiveOnly: true,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
items := make([]userResourceData, 0, len(resp.GetResources()))
|
|
for _, item := range resp.GetResources() {
|
|
items = append(items, userResourceFromProto(item))
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items)})
|
|
}
|
|
|
|
func (h *Handler) equipMyResource(writer http.ResponseWriter, request *http.Request) {
|
|
if request.Method != http.MethodPost {
|
|
httpkit.WriteError(writer, request, http.StatusMethodNotAllowed, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
if h.walletClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
resourceID, ok := httpkit.PositiveInt64PathValue(request, "resource_id")
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
var body struct {
|
|
EntitlementID string `json:"entitlement_id"`
|
|
}
|
|
if request.Body != nil && request.Body != http.NoBody {
|
|
if err := httpkit.DecodeJSON(request.Body, &body); err != nil && !errors.Is(err, io.EOF) {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidJSON, "invalid request body")
|
|
return
|
|
}
|
|
}
|
|
resp, err := h.walletClient.EquipUserResource(request.Context(), &walletv1.EquipUserResourceRequest{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
ResourceId: resourceID,
|
|
EntitlementId: strings.TrimSpace(body.EntitlementID),
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
httpkit.WriteOK(writer, request, userResourceFromProto(resp.GetResource()))
|
|
}
|
|
|
|
func resourceFromProto(item *walletv1.Resource) resourceData {
|
|
if item == nil {
|
|
return resourceData{}
|
|
}
|
|
return resourceData{
|
|
ResourceID: item.GetResourceId(),
|
|
ResourceCode: item.GetResourceCode(),
|
|
ResourceType: item.GetResourceType(),
|
|
Name: item.GetName(),
|
|
Status: item.GetStatus(),
|
|
Grantable: item.GetGrantable(),
|
|
GrantStrategy: item.GetGrantStrategy(),
|
|
WalletAssetType: item.GetWalletAssetType(),
|
|
WalletAssetAmount: item.GetWalletAssetAmount(),
|
|
UsageScopes: item.GetUsageScopes(),
|
|
AssetURL: item.GetAssetUrl(),
|
|
PreviewURL: item.GetPreviewUrl(),
|
|
AnimationURL: item.GetAnimationUrl(),
|
|
MetadataJSON: item.GetMetadataJson(),
|
|
SortOrder: item.GetSortOrder(),
|
|
CreatedAtMS: item.GetCreatedAtMs(),
|
|
UpdatedAtMS: item.GetUpdatedAtMs(),
|
|
}
|
|
}
|
|
|
|
func resourceGroupFromProto(group *walletv1.ResourceGroup) resourceGroupData {
|
|
if group == nil {
|
|
return resourceGroupData{}
|
|
}
|
|
items := make([]resourceGroupItemData, 0, len(group.GetItems()))
|
|
for _, item := range group.GetItems() {
|
|
items = append(items, resourceGroupItemData{
|
|
GroupItemID: item.GetGroupItemId(),
|
|
ItemType: item.GetItemType(),
|
|
ResourceID: item.GetResourceId(),
|
|
Resource: resourcePointerFromProto(item.GetResource()),
|
|
WalletAssetType: item.GetWalletAssetType(),
|
|
WalletAssetAmount: item.GetWalletAssetAmount(),
|
|
Quantity: item.GetQuantity(),
|
|
DurationMS: item.GetDurationMs(),
|
|
SortOrder: item.GetSortOrder(),
|
|
})
|
|
}
|
|
return resourceGroupData{
|
|
GroupID: group.GetGroupId(),
|
|
GroupCode: group.GetGroupCode(),
|
|
Name: group.GetName(),
|
|
Status: group.GetStatus(),
|
|
Description: group.GetDescription(),
|
|
SortOrder: group.GetSortOrder(),
|
|
Items: items,
|
|
CreatedAtMS: group.GetCreatedAtMs(),
|
|
UpdatedAtMS: group.GetUpdatedAtMs(),
|
|
}
|
|
}
|
|
|
|
func resourcePointerFromProto(item *walletv1.Resource) *resourceData {
|
|
if item == nil || item.GetResourceId() == 0 {
|
|
return nil
|
|
}
|
|
data := resourceFromProto(item)
|
|
return &data
|
|
}
|
|
|
|
func giftFromProto(gift *walletv1.GiftConfig) giftConfigData {
|
|
if gift == nil {
|
|
return giftConfigData{}
|
|
}
|
|
return giftConfigData{
|
|
GiftID: gift.GetGiftId(),
|
|
ResourceID: gift.GetResourceId(),
|
|
Resource: resourceFromProto(gift.GetResource()),
|
|
Status: gift.GetStatus(),
|
|
Name: gift.GetName(),
|
|
SortOrder: gift.GetSortOrder(),
|
|
PresentationJSON: gift.GetPresentationJson(),
|
|
PriceVersion: gift.GetPriceVersion(),
|
|
GiftTypeCode: gift.GetGiftTypeCode(),
|
|
ChargeAssetType: gift.GetChargeAssetType(),
|
|
CoinPrice: gift.GetCoinPrice(),
|
|
GiftPointAmount: gift.GetGiftPointAmount(),
|
|
HeatValue: gift.GetHeatValue(),
|
|
EffectiveFromMS: gift.GetEffectiveFromMs(),
|
|
EffectiveToMS: gift.GetEffectiveToMs(),
|
|
EffectTypes: gift.GetEffectTypes(),
|
|
CreatedAtMS: gift.GetCreatedAtMs(),
|
|
UpdatedAtMS: gift.GetUpdatedAtMs(),
|
|
RegionIDs: gift.GetRegionIds(),
|
|
}
|
|
}
|
|
|
|
func userResourceFromProto(item *walletv1.UserResourceEntitlement) userResourceData {
|
|
if item == nil {
|
|
return userResourceData{}
|
|
}
|
|
return userResourceData{
|
|
EntitlementID: item.GetEntitlementId(),
|
|
ResourceID: item.GetResourceId(),
|
|
Resource: resourceFromProto(item.GetResource()),
|
|
Status: item.GetStatus(),
|
|
Quantity: item.GetQuantity(),
|
|
RemainingQuantity: item.GetRemainingQuantity(),
|
|
EffectiveAtMS: item.GetEffectiveAtMs(),
|
|
ExpiresAtMS: item.GetExpiresAtMs(),
|
|
SourceGrantID: item.GetSourceGrantId(),
|
|
CreatedAtMS: item.GetCreatedAtMs(),
|
|
UpdatedAtMS: item.GetUpdatedAtMs(),
|
|
}
|
|
}
|
|
|
|
func emojiPackFromProto(item *walletv1.Resource) emojiPackData {
|
|
if item == nil {
|
|
return emojiPackData{}
|
|
}
|
|
metadata := emojiPackMetadataFromJSON(item.GetMetadataJson())
|
|
return emojiPackData{
|
|
ResourceID: item.GetResourceId(),
|
|
ResourceCode: item.GetResourceCode(),
|
|
Name: item.GetName(),
|
|
Category: metadata.Category,
|
|
PricingType: metadata.PricingType,
|
|
CoverURL: firstNonEmpty(item.GetPreviewUrl(), item.GetAssetUrl()),
|
|
AnimationURL: item.GetAnimationUrl(),
|
|
RegionIDs: metadata.RegionIDs,
|
|
IsGlobal: len(metadata.RegionIDs) == 0,
|
|
SortOrder: item.GetSortOrder(),
|
|
UpdatedAtMS: item.GetUpdatedAtMs(),
|
|
}
|
|
}
|
|
|
|
func emojiPackMetadataFromJSON(metadataJSON string) emojiPackMetadata {
|
|
payload := emojiPackMetadata{
|
|
Category: defaultEmojiPackCategory,
|
|
PricingType: emojiPackPricingFree,
|
|
}
|
|
if err := json.Unmarshal([]byte(metadataJSON), &payload); err != nil {
|
|
return payload
|
|
}
|
|
payload.Category = normalizeEmojiPackCategory(payload.Category)
|
|
payload.PricingType = normalizeEmojiPackPricingType(payload.PricingType)
|
|
payload.RegionIDs = normalizeEmojiPackRegionIDs(payload.RegionIDs)
|
|
return payload
|
|
}
|
|
|
|
func normalizeEmojiPackCategory(value string) string {
|
|
category := strings.TrimSpace(value)
|
|
if category == "" {
|
|
return defaultEmojiPackCategory
|
|
}
|
|
return category
|
|
}
|
|
|
|
func normalizeEmojiPackPricingType(value string) string {
|
|
if strings.ToLower(strings.TrimSpace(value)) == emojiPackPricingPaid {
|
|
return emojiPackPricingPaid
|
|
}
|
|
return emojiPackPricingFree
|
|
}
|
|
|
|
func normalizeEmojiPackRegionIDs(regionIDs []int64) []int64 {
|
|
out := make([]int64, 0, len(regionIDs))
|
|
seen := make(map[int64]struct{}, len(regionIDs))
|
|
for _, regionID := range regionIDs {
|
|
if regionID <= 0 {
|
|
return nil
|
|
}
|
|
if _, ok := seen[regionID]; ok {
|
|
continue
|
|
}
|
|
seen[regionID] = struct{}{}
|
|
out = append(out, regionID)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func emojiPackMatchesRegion(pack emojiPackData, regionID int64) bool {
|
|
if pack.IsGlobal || regionID == 0 {
|
|
return true
|
|
}
|
|
for _, candidate := range pack.RegionIDs {
|
|
if candidate == regionID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func paginateEmojiPacks(items []emojiPackData, page int32, pageSize int32) []emojiPackData {
|
|
start := int((page - 1) * pageSize)
|
|
if start >= len(items) {
|
|
return []emojiPackData{}
|
|
}
|
|
end := start + int(pageSize)
|
|
if end > len(items) {
|
|
end = len(items)
|
|
}
|
|
return items[start:end]
|
|
}
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return value
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func resourcePage(request *http.Request) (int32, int32, bool) {
|
|
page, ok := httpkit.PositiveInt32Query(request, "page", 1)
|
|
if !ok {
|
|
return 0, 0, false
|
|
}
|
|
pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 20)
|
|
if !ok {
|
|
return 0, 0, false
|
|
}
|
|
if raw := strings.TrimSpace(request.URL.Query().Get("pageSize")); raw != "" {
|
|
parsed, err := strconv.Atoi(raw)
|
|
if err != nil || parsed <= 0 {
|
|
return 0, 0, false
|
|
}
|
|
pageSize = int32(parsed)
|
|
}
|
|
return page, pageSize, true
|
|
}
|
|
|
|
func optionalNonNegativeInt64Query(request *http.Request, name string) (int64, bool, bool) {
|
|
raw := strings.TrimSpace(request.URL.Query().Get(name))
|
|
if raw == "" {
|
|
return 0, false, true
|
|
}
|
|
value, err := strconv.ParseInt(raw, 10, 64)
|
|
if err != nil || value < 0 {
|
|
return 0, false, false
|
|
}
|
|
return value, true, true
|
|
}
|