2026-05-12 09:53:20 +08:00

401 lines
14 KiB
Go

package http
import (
"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"
)
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 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) 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 resourcePage(request *http.Request) (int32, int32, bool) {
page, ok := parsePositiveInt32Query(request, "page", 1)
if !ok {
return 0, 0, false
}
pageSize, ok := parsePositiveInt32Query(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 parsePositiveInt32Query(request *http.Request, name string, fallback int32) (int32, bool) {
raw := strings.TrimSpace(request.URL.Query().Get(name))
if raw == "" {
return fallback, true
}
value, err := strconv.Atoi(raw)
if err != nil || value <= 0 {
return 0, false
}
return int32(value), 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
}