feat: 添加实时查询余额的接口

This commit is contained in:
zerosaturation 2026-05-19 18:50:57 +08:00
parent eaad2642a8
commit 7b8fba5701
4 changed files with 162 additions and 18 deletions

View File

@ -32,25 +32,43 @@ import (
"go.uber.org/zap"
"github.com/topfans/backend/pkg/logger"
pbGallery "github.com/topfans/backend/pkg/proto/gallery"
pbUser "github.com/topfans/backend/pkg/proto/user"
"github.com/topfans/backend/services/assetService/service"
)
// AssetController 资产相关控制器
type AssetController struct {
assetService pbAsset.AssetService
userService pbUser.UserSocialService
galleryService pbGallery.GalleryService
minimaxService service.MinimaxService
}
// NewAssetController 创建资产控制器
func NewAssetController(dubboClient *client.Client) (*AssetController, error) {
func NewAssetController(assetClient *client.Client, userClient *client.Client, galleryClient *client.Client) (*AssetController, error) {
// 创建 AssetService 客户端
assetService, err := pbAsset.NewAssetService(dubboClient)
assetService, err := pbAsset.NewAssetService(assetClient)
if err != nil {
return nil, err
}
// 创建 UserService 客户端
userService, err := pbUser.NewUserSocialService(userClient)
if err != nil {
return nil, err
}
// 创建 GalleryService 客户端
galleryService, err := pbGallery.NewGalleryService(galleryClient)
if err != nil {
return nil, err
}
return &AssetController{
assetService: assetService,
userService: userService,
galleryService: galleryService,
minimaxService: service.NewMinimaxService(),
}, nil
}
@ -264,11 +282,11 @@ func (ctrl *AssetController) EstimateMintCost(c *gin.Context) {
}
data := map[string]interface{}{
"cost_crystal": resp.CostCrystal,
"current_balance": resp.CurrentBalance,
"balance_after": resp.BalanceAfter,
"mint_count": resp.MintCount,
"next_tier_hint": resp.NextTierHint,
"cost_crystal": resp.CostCrystal,
"current_balance": resp.CurrentBalance,
"balance_after": resp.BalanceAfter,
"mint_count": resp.MintCount,
"next_tier_hint": resp.NextTierHint,
}
response.Success(c, data)
}
@ -1585,7 +1603,8 @@ func (ctrl *AssetController) BindAssetMaterials(c *gin.Context) {
uid, _ := userIDVal.(int64)
starIDVal, _ := c.Get("star_id")
sid, _ := starIDVal.(int64)
_ = uid; _ = sid // suppress unused
_ = uid
_ = sid // suppress unused
var req dto.BindAssetMaterialsRequestDTO
if err := c.ShouldBindJSON(&req); err != nil {
@ -1786,3 +1805,98 @@ func (ctrl *AssetController) UnbindAssetMaterial(c *gin.Context) {
response.Success(c, gin.H{"message": "解绑成功"})
}
// GetEarningsSummary 获取收益汇总
// @Summary 获取收益汇总
// @Description 获取当前用户的收益汇总(总每小时收益、总展出收益、水晶余额)
// @Tags assets
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} response.Response
// @Router /api/v1/assets/me/earnings-summary [get]
func (ctrl *AssetController) GetEarningsSummary(c *gin.Context) {
userIDVal, _ := c.Get("user_id")
starIDVal, _ := c.Get("star_id")
userID, ok := userIDVal.(int64)
if !ok {
logger.Logger.Error("GetEarningsSummary: user_id type assertion failed", zap.Any("value", userIDVal))
response.Error(c, http.StatusInternalServerError, "用户ID无效")
return
}
starID, ok := starIDVal.(int64)
if !ok {
logger.Logger.Error("GetEarningsSummary: star_id type assertion failed", zap.Any("value", starIDVal))
response.Error(c, http.StatusInternalServerError, "明星ID无效")
return
}
logger.Logger.Info("GetEarningsSummary: start", zap.Int64("user_id", userID), zap.Int64("star_id", starID))
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID, 10),
"star_id": strconv.FormatInt(starID, 10),
})
// 1. 获取我展出的作品列表
var totalHourlyEarnings float64
var totalExhibitionRevenue int64
page := int32(1)
pageSize := int32(200)
for {
resp, err := ctrl.galleryService.GetMyExhibitedAssets(ctx, &pbGallery.GetMyExhibitedAssetsRequest{
Page: page,
PageSize: pageSize,
})
if err != nil {
logger.Logger.Error("GetEarningsSummary: GetMyExhibitedAssets failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "获取展出作品列表失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
logger.Logger.Warn("GetEarningsSummary: GetMyExhibitedAssets returned non-OK", zap.Any("code", resp.Base.Code), zap.String("msg", resp.Base.Message))
}
// 累加每个 item 的收益GetMyExhibitedAssets 返回的都是展出中的作品)
for _, item := range resp.Data.Items {
hourlyEarnings := item.HourlyEarnings
totalHourlyEarnings += hourlyEarnings
totalExhibitionRevenue += item.Earnings
}
// 检查是否还有更多
if !resp.Data.HasMore {
break
}
page++
}
logger.Logger.Info("GetEarningsSummary: assets fetched", zap.Float64("total_hourly", totalHourlyEarnings), zap.Int64("total_revenue", totalExhibitionRevenue))
// 2. 获取水晶余额
logger.Logger.Info("GetEarningsSummary: calling GetFanProfile", zap.Int64("user_id", userID), zap.Int64("star_id", starID))
userResp, err := ctrl.userService.GetFanProfile(ctx, &pbUser.GetFanProfileRequest{
UserId: userID,
StarId: starID,
})
if err != nil {
logger.Logger.Error("GetEarningsSummary: GetFanProfile failed", zap.Error(err), zap.Int64("user_id", userID), zap.Int64("star_id", starID))
response.Error(c, http.StatusInternalServerError, "获取用户信息失败")
return
}
logger.Logger.Info("GetEarningsSummary: success", zap.Float64("total_hourly", totalHourlyEarnings), zap.Int64("total_revenue", totalExhibitionRevenue), zap.Int64("balance", userResp.GetProfile().GetCrystalBalance()))
response.Success(c, gin.H{
"total_hourly_earnings": totalHourlyEarnings,
"total_exhibition_revenue": totalExhibitionRevenue,
"crystal_balance": userResp.GetProfile().GetCrystalBalance(),
})
}

View File

@ -46,7 +46,7 @@ func SetupRouter(userClient *client.Client, socialClient *client.Client, assetCl
return nil, err
}
assetCtrl, err := controller.NewAssetController(assetClient)
assetCtrl, err := controller.NewAssetController(assetClient, userClient, galleryClient)
if err != nil {
return nil, err
}
@ -192,6 +192,7 @@ func SetupRouter(userClient *client.Client, socialClient *client.Client, assetCl
assets.GET("/mints/:order_id", assetCtrl.GetMintOrder) // 查询铸造订单状态
assets.DELETE("/mints/:order_id", assetCtrl.CancelMintOrder) // 取消铸造订单
assets.POST("/mints/image/generation", assetCtrl.ImageGeneration) // 图生图
assets.GET("/me/earnings-summary", assetCtrl.GetEarningsSummary) // 获取收益汇总
assets.GET("/me/items", assetCtrl.GetMyAssets) // 获取我的藏品列表
assets.GET("/:asset_id", assetCtrl.GetAsset) // 获取资产详情
assets.GET("/:asset_id/status", assetCtrl.GetAssetStatus) // 查询上链状态

View File

@ -69,12 +69,12 @@
<!-- 上层文字内容 -->
<view class="crystal-text-layer">
<text class="balance-number">{{ crystalBalance }}</text>
<text class="balance-number">{{ exhibitionRevenue }}</text>
</view>
<!-- 收益文字 -->
<view class="crystal-bg-layer">
<text class="balance-income">收益 0/H</text>
<text class="balance-income">收益 {{ hourlyEarnings }}/</text>
</view>
</view>
@ -103,6 +103,7 @@ import DailyTasks from '@/pages/tasks/daily-tasks.vue';
import GuideModal from '@/pages/tasks/GuideModal.vue';
import { useBanner } from '@/pages/square/composables/useBanner.js';
import { reportEvent } from '@/utils/task-api.js';
import { getEarningsSummaryApi } from '@/utils/api.js';
// square useBanner
const { bannerActivities, loadBannerActivities } = useBanner();
@ -170,10 +171,29 @@ const userAvatarUrl = computed(() => {
return userInfo.value?.avatar_url || '';
});
//
const crystalBalance = computed(() => {
return userInfo.value?.crystal_balance ?? 9527;
});
// //
// const crystalBalance = computed(() => {
// return userInfo.value?.crystal_balance ?? 9527;
// });
//
const exhibitionRevenue = ref(0); //
const hourlyEarnings = ref(0); //
//
const loadEarningsSummary = async () => {
try {
const response = await getEarningsSummaryApi();
if (response.code === 200) {
const data = response.data;
exhibitionRevenue.value = data.crystal_balance || 0;
hourlyEarnings.value = data.total_hourly_earnings || 0;
}
} catch (e) {
console.error('获取收益汇总失败:', e);
}
};
//
const handleAvatarUpdate = (data) => {
@ -243,9 +263,10 @@ const starActivities = computed(() => {
});
//
onMounted(() => {
loadUserInfo();
loadBannerActivities();
onMounted(async() => {
await loadUserInfo();
await loadBannerActivities();
await loadEarningsSummary();
uni.$on('avatarUpdated', handleAvatarUpdate);
uni.$on('userInfoUpdated', handleUserInfoUpdate);
uni.$on('balanceUpdated', handleBalanceUpdate);

View File

@ -798,4 +798,12 @@ export function getMintingActivitiesApi(starId = null, page = 1, pageSize = 10)
url: url,
method: 'GET'
})
}
// 获取收益汇总接口
export function getEarningsSummaryApi() {
return request({
url: '/api/v1/assets/me/earnings-summary',
method: 'GET'
})
}