feat: 修改铸造消耗确认框和新增批量购买道具的功能,修改回退按钮的颜色

This commit is contained in:
zerosaturation 2026-05-22 00:14:28 +08:00
parent 569321bb41
commit ee0433464f
29 changed files with 1307 additions and 414 deletions

View File

@ -343,6 +343,131 @@ func (ctrl *ActivityController) PurchaseItem(c *gin.Context) {
response.Success(c, data)
}
// BatchPurchaseItem 批量购买道具
// @Summary 批量购买道具
// @Description 批量购买活动道具
// @Tags activities
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param activity_id path int64 true "活动ID"
// @Param request body pbActivity.BatchPurchaseItemRequest true "批量购买请求"
// @Success 200 {object} response.Response
// @Router /api/v1/activities/{activity_id}/batch-purchase [post]
func (ctrl *ActivityController) BatchPurchaseItem(c *gin.Context) {
// 从上下文获取用户信息
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
// 解析路径参数
activityIDStr := c.Param("id")
activityID, err := strconv.ParseInt(activityIDStr, 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, "活动ID参数错误")
return
}
// 解析请求体
var req struct {
Items []struct {
ItemType string `json:"item_type"`
Quantity int `json:"quantity"`
} `json:"items"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "请求参数错误")
return
}
if len(req.Items) == 0 {
response.Error(c, http.StatusBadRequest, "items 是必填参数")
return
}
logger.Logger.Info("BatchPurchaseItem request",
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
zap.Int64("activity_id", activityID),
zap.Int("items_count", len(req.Items)),
)
// 设置上下文
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.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 转换请求
pbItems := make([]*pbActivity.PurchaseItem, len(req.Items))
for i, item := range req.Items {
quantity := int32(item.Quantity)
if quantity <= 0 {
quantity = 1
}
pbItems[i] = &pbActivity.PurchaseItem{
ItemType: item.ItemType,
Quantity: quantity,
}
}
// 调用 RPC
resp, err := ctrl.activityService.BatchPurchaseItem(ctx, &pbActivity.BatchPurchaseItemRequest{
ActivityId: activityID,
Items: pbItems,
StarId: starID.(int64),
UserId: userID.(int64),
})
if err != nil {
logger.Logger.Error("BatchPurchaseItem RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "批量购买道具失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 非 active 阶段:通过 message 信号返回 200 + activity_status
activityStatusMap := map[string]string{
"activity:expired": "expired",
"activity:pending": "pending",
"activity:completed": "completed",
"activity:incomplete": "incomplete",
"activity:active": "active",
}
activityMessageMap := map[string]string{
"expired": "活动已结束",
"pending": "活动未开始",
"completed": "活动已完成",
"incomplete": "活动未完成",
"active": "活动进行中",
}
if status, ok := activityStatusMap[resp.Base.Message]; ok {
response.Success(c, map[string]interface{}{
"activity_status": status,
"message": activityMessageMap[status],
})
return
}
// 正常购买成功
data := convertBatchPurchaseResponse(resp)
response.Success(c, data)
}
// GetContributionRanking 获取贡献点排名
// @Summary 获取贡献点排名
// @Description 获取活动贡献点排名
@ -742,6 +867,26 @@ func convertPurchaseResponse(resp *pbActivity.PurchaseItemResponse) map[string]i
}
}
// convertBatchPurchaseResponse 转换批量购买响应
func convertBatchPurchaseResponse(resp *pbActivity.BatchPurchaseItemResponse) map[string]interface{} {
fails := make([]map[string]interface{}, 0, len(resp.Fails))
for _, fail := range resp.Fails {
fails = append(fails, map[string]interface{}{
"item_type": fail.ItemType,
"reason": fail.Reason,
})
}
return map[string]interface{}{
"total_crystal_spent": resp.TotalCrystalSpent,
"total_contribution": resp.TotalContribution,
"current_progress": resp.CurrentProgress,
"remaining_balance": resp.RemainingBalance,
"success_count": resp.SuccessCount,
"fail_count": resp.FailCount,
"fails": fails,
}
}
// convertContributionRankingResponse 转换排名响应
func convertContributionRankingResponse(resp *pbActivity.ContributionRankingResponse) map[string]interface{} {
items := make([]map[string]interface{}, 0, len(resp.Items))

View File

@ -286,7 +286,7 @@ func (ctrl *AssetController) EstimateMintCost(c *gin.Context) {
"current_balance": resp.CurrentBalance,
"balance_after": resp.BalanceAfter,
"mint_count": resp.MintCount,
"next_tier_hint": resp.NextTierHint,
"next_tier_cost": resp.NextTierCost,
}
response.Success(c, data)
}

View File

@ -249,6 +249,7 @@ func SetupRouter(userClient *client.Client, socialClient *client.Client, assetCl
activities.GET("/:id/items", activityCtrl.GetActivityItems) // 获取活动道具列表
activities.GET("/:id/progress", activityCtrl.GetProgress) // 获取活动进度
activities.POST("/:id/purchase", activityCtrl.PurchaseItem) // 购买道具
activities.POST("/:id/batch-purchase", activityCtrl.BatchPurchaseItem) // 批量购买道具
activities.GET("/:id/ranking", activityCtrl.GetContributionRanking) // 获取贡献点排名
activities.GET("/:id/contributions/latest", activityCtrl.GetLatestContributions) // 获取最新贡献记录
}

View File

@ -496,6 +496,282 @@ func (x *PurchaseItemResponse) GetRemainingBalance() int64 {
return 0
}
// 单个购买项(用于批量购买)
type PurchaseItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
ItemType string `protobuf:"bytes,1,opt,name=item_type,json=itemType,proto3" json:"item_type,omitempty"`
Quantity int32 `protobuf:"varint,2,opt,name=quantity,proto3" json:"quantity,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PurchaseItem) Reset() {
*x = PurchaseItem{}
mi := &file_activity_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PurchaseItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PurchaseItem) ProtoMessage() {}
func (x *PurchaseItem) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PurchaseItem.ProtoReflect.Descriptor instead.
func (*PurchaseItem) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{5}
}
func (x *PurchaseItem) GetItemType() string {
if x != nil {
return x.ItemType
}
return ""
}
func (x *PurchaseItem) GetQuantity() int32 {
if x != nil {
return x.Quantity
}
return 0
}
// 批量购买道具请求
type BatchPurchaseItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
ActivityId int64 `protobuf:"varint,1,opt,name=activity_id,json=activityId,proto3" json:"activity_id,omitempty"`
Items []*PurchaseItem `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"` // 购买项列表
StarId int64 `protobuf:"varint,3,opt,name=star_id,json=starId,proto3" json:"star_id,omitempty"`
UserId int64 `protobuf:"varint,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // 当前用户ID
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BatchPurchaseItemRequest) Reset() {
*x = BatchPurchaseItemRequest{}
mi := &file_activity_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BatchPurchaseItemRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchPurchaseItemRequest) ProtoMessage() {}
func (x *BatchPurchaseItemRequest) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BatchPurchaseItemRequest.ProtoReflect.Descriptor instead.
func (*BatchPurchaseItemRequest) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{6}
}
func (x *BatchPurchaseItemRequest) GetActivityId() int64 {
if x != nil {
return x.ActivityId
}
return 0
}
func (x *BatchPurchaseItemRequest) GetItems() []*PurchaseItem {
if x != nil {
return x.Items
}
return nil
}
func (x *BatchPurchaseItemRequest) GetStarId() int64 {
if x != nil {
return x.StarId
}
return 0
}
func (x *BatchPurchaseItemRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
// 批量购买道具响应
type BatchPurchaseItemResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Base *common.BaseResponse `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
TotalCrystalSpent int64 `protobuf:"varint,2,opt,name=total_crystal_spent,json=totalCrystalSpent,proto3" json:"total_crystal_spent,omitempty"` // 本次消费水晶
TotalContribution int64 `protobuf:"varint,3,opt,name=total_contribution,json=totalContribution,proto3" json:"total_contribution,omitempty"` // 本次获得贡献点
CurrentProgress int64 `protobuf:"varint,4,opt,name=current_progress,json=currentProgress,proto3" json:"current_progress,omitempty"` // 当前活动进度
RemainingBalance int64 `protobuf:"varint,5,opt,name=remaining_balance,json=remainingBalance,proto3" json:"remaining_balance,omitempty"` // 剩余水晶余额
SuccessCount int32 `protobuf:"varint,6,opt,name=success_count,json=successCount,proto3" json:"success_count,omitempty"` // 成功购买数量
FailCount int32 `protobuf:"varint,7,opt,name=fail_count,json=failCount,proto3" json:"fail_count,omitempty"` // 失败购买数量
Fails []*PurchaseFailItem `protobuf:"bytes,8,rep,name=fails,proto3" json:"fails,omitempty"` // 失败的项
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BatchPurchaseItemResponse) Reset() {
*x = BatchPurchaseItemResponse{}
mi := &file_activity_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BatchPurchaseItemResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchPurchaseItemResponse) ProtoMessage() {}
func (x *BatchPurchaseItemResponse) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BatchPurchaseItemResponse.ProtoReflect.Descriptor instead.
func (*BatchPurchaseItemResponse) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{7}
}
func (x *BatchPurchaseItemResponse) GetBase() *common.BaseResponse {
if x != nil {
return x.Base
}
return nil
}
func (x *BatchPurchaseItemResponse) GetTotalCrystalSpent() int64 {
if x != nil {
return x.TotalCrystalSpent
}
return 0
}
func (x *BatchPurchaseItemResponse) GetTotalContribution() int64 {
if x != nil {
return x.TotalContribution
}
return 0
}
func (x *BatchPurchaseItemResponse) GetCurrentProgress() int64 {
if x != nil {
return x.CurrentProgress
}
return 0
}
func (x *BatchPurchaseItemResponse) GetRemainingBalance() int64 {
if x != nil {
return x.RemainingBalance
}
return 0
}
func (x *BatchPurchaseItemResponse) GetSuccessCount() int32 {
if x != nil {
return x.SuccessCount
}
return 0
}
func (x *BatchPurchaseItemResponse) GetFailCount() int32 {
if x != nil {
return x.FailCount
}
return 0
}
func (x *BatchPurchaseItemResponse) GetFails() []*PurchaseFailItem {
if x != nil {
return x.Fails
}
return nil
}
// 购买失败的项
type PurchaseFailItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
ItemType string `protobuf:"bytes,1,opt,name=item_type,json=itemType,proto3" json:"item_type,omitempty"`
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PurchaseFailItem) Reset() {
*x = PurchaseFailItem{}
mi := &file_activity_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PurchaseFailItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PurchaseFailItem) ProtoMessage() {}
func (x *PurchaseFailItem) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PurchaseFailItem.ProtoReflect.Descriptor instead.
func (*PurchaseFailItem) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{8}
}
func (x *PurchaseFailItem) GetItemType() string {
if x != nil {
return x.ItemType
}
return ""
}
func (x *PurchaseFailItem) GetReason() string {
if x != nil {
return x.Reason
}
return ""
}
// 贡献点排名请求
type ContributionRankingRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -510,7 +786,7 @@ type ContributionRankingRequest struct {
func (x *ContributionRankingRequest) Reset() {
*x = ContributionRankingRequest{}
mi := &file_activity_proto_msgTypes[5]
mi := &file_activity_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -522,7 +798,7 @@ func (x *ContributionRankingRequest) String() string {
func (*ContributionRankingRequest) ProtoMessage() {}
func (x *ContributionRankingRequest) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[5]
mi := &file_activity_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -535,7 +811,7 @@ func (x *ContributionRankingRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ContributionRankingRequest.ProtoReflect.Descriptor instead.
func (*ContributionRankingRequest) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{5}
return file_activity_proto_rawDescGZIP(), []int{9}
}
func (x *ContributionRankingRequest) GetActivityId() int64 {
@ -588,7 +864,7 @@ type ContributionRankingItem struct {
func (x *ContributionRankingItem) Reset() {
*x = ContributionRankingItem{}
mi := &file_activity_proto_msgTypes[6]
mi := &file_activity_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -600,7 +876,7 @@ func (x *ContributionRankingItem) String() string {
func (*ContributionRankingItem) ProtoMessage() {}
func (x *ContributionRankingItem) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[6]
mi := &file_activity_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -613,7 +889,7 @@ func (x *ContributionRankingItem) ProtoReflect() protoreflect.Message {
// Deprecated: Use ContributionRankingItem.ProtoReflect.Descriptor instead.
func (*ContributionRankingItem) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{6}
return file_activity_proto_rawDescGZIP(), []int{10}
}
func (x *ContributionRankingItem) GetRank() int32 {
@ -673,7 +949,7 @@ type ContributionRankingResponse struct {
func (x *ContributionRankingResponse) Reset() {
*x = ContributionRankingResponse{}
mi := &file_activity_proto_msgTypes[7]
mi := &file_activity_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -685,7 +961,7 @@ func (x *ContributionRankingResponse) String() string {
func (*ContributionRankingResponse) ProtoMessage() {}
func (x *ContributionRankingResponse) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[7]
mi := &file_activity_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -698,7 +974,7 @@ func (x *ContributionRankingResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ContributionRankingResponse.ProtoReflect.Descriptor instead.
func (*ContributionRankingResponse) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{7}
return file_activity_proto_rawDescGZIP(), []int{11}
}
func (x *ContributionRankingResponse) GetBase() *common.BaseResponse {
@ -758,7 +1034,7 @@ type MyContribution struct {
func (x *MyContribution) Reset() {
*x = MyContribution{}
mi := &file_activity_proto_msgTypes[8]
mi := &file_activity_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -770,7 +1046,7 @@ func (x *MyContribution) String() string {
func (*MyContribution) ProtoMessage() {}
func (x *MyContribution) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[8]
mi := &file_activity_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -783,7 +1059,7 @@ func (x *MyContribution) ProtoReflect() protoreflect.Message {
// Deprecated: Use MyContribution.ProtoReflect.Descriptor instead.
func (*MyContribution) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{8}
return file_activity_proto_rawDescGZIP(), []int{12}
}
func (x *MyContribution) GetRank() int32 {
@ -841,7 +1117,7 @@ type GetActivityListRequest struct {
func (x *GetActivityListRequest) Reset() {
*x = GetActivityListRequest{}
mi := &file_activity_proto_msgTypes[9]
mi := &file_activity_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -853,7 +1129,7 @@ func (x *GetActivityListRequest) String() string {
func (*GetActivityListRequest) ProtoMessage() {}
func (x *GetActivityListRequest) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[9]
mi := &file_activity_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -866,7 +1142,7 @@ func (x *GetActivityListRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetActivityListRequest.ProtoReflect.Descriptor instead.
func (*GetActivityListRequest) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{9}
return file_activity_proto_rawDescGZIP(), []int{13}
}
func (x *GetActivityListRequest) GetStarId() int64 {
@ -911,7 +1187,7 @@ type GetActivityListResponse struct {
func (x *GetActivityListResponse) Reset() {
*x = GetActivityListResponse{}
mi := &file_activity_proto_msgTypes[10]
mi := &file_activity_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -923,7 +1199,7 @@ func (x *GetActivityListResponse) String() string {
func (*GetActivityListResponse) ProtoMessage() {}
func (x *GetActivityListResponse) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[10]
mi := &file_activity_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -936,7 +1212,7 @@ func (x *GetActivityListResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetActivityListResponse.ProtoReflect.Descriptor instead.
func (*GetActivityListResponse) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{10}
return file_activity_proto_rawDescGZIP(), []int{14}
}
func (x *GetActivityListResponse) GetBase() *common.BaseResponse {
@ -984,7 +1260,7 @@ type GetProgressRequest struct {
func (x *GetProgressRequest) Reset() {
*x = GetProgressRequest{}
mi := &file_activity_proto_msgTypes[11]
mi := &file_activity_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -996,7 +1272,7 @@ func (x *GetProgressRequest) String() string {
func (*GetProgressRequest) ProtoMessage() {}
func (x *GetProgressRequest) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[11]
mi := &file_activity_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1009,7 +1285,7 @@ func (x *GetProgressRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetProgressRequest.ProtoReflect.Descriptor instead.
func (*GetProgressRequest) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{11}
return file_activity_proto_rawDescGZIP(), []int{15}
}
func (x *GetProgressRequest) GetActivityId() int64 {
@ -1035,7 +1311,7 @@ type GetProgressResponse struct {
func (x *GetProgressResponse) Reset() {
*x = GetProgressResponse{}
mi := &file_activity_proto_msgTypes[12]
mi := &file_activity_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1047,7 +1323,7 @@ func (x *GetProgressResponse) String() string {
func (*GetProgressResponse) ProtoMessage() {}
func (x *GetProgressResponse) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[12]
mi := &file_activity_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1060,7 +1336,7 @@ func (x *GetProgressResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetProgressResponse.ProtoReflect.Descriptor instead.
func (*GetProgressResponse) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{12}
return file_activity_proto_rawDescGZIP(), []int{16}
}
func (x *GetProgressResponse) GetBase() *common.BaseResponse {
@ -1130,7 +1406,7 @@ type MintingActivity struct {
func (x *MintingActivity) Reset() {
*x = MintingActivity{}
mi := &file_activity_proto_msgTypes[13]
mi := &file_activity_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1142,7 +1418,7 @@ func (x *MintingActivity) String() string {
func (*MintingActivity) ProtoMessage() {}
func (x *MintingActivity) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[13]
mi := &file_activity_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1155,7 +1431,7 @@ func (x *MintingActivity) ProtoReflect() protoreflect.Message {
// Deprecated: Use MintingActivity.ProtoReflect.Descriptor instead.
func (*MintingActivity) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{13}
return file_activity_proto_rawDescGZIP(), []int{17}
}
func (x *MintingActivity) GetId() int64 {
@ -1233,7 +1509,7 @@ type GetMintingActivitiesRequest struct {
func (x *GetMintingActivitiesRequest) Reset() {
*x = GetMintingActivitiesRequest{}
mi := &file_activity_proto_msgTypes[14]
mi := &file_activity_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1245,7 +1521,7 @@ func (x *GetMintingActivitiesRequest) String() string {
func (*GetMintingActivitiesRequest) ProtoMessage() {}
func (x *GetMintingActivitiesRequest) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[14]
mi := &file_activity_proto_msgTypes[18]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1258,7 +1534,7 @@ func (x *GetMintingActivitiesRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetMintingActivitiesRequest.ProtoReflect.Descriptor instead.
func (*GetMintingActivitiesRequest) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{14}
return file_activity_proto_rawDescGZIP(), []int{18}
}
func (x *GetMintingActivitiesRequest) GetStarId() int64 {
@ -1296,7 +1572,7 @@ type GetMintingActivitiesResponse struct {
func (x *GetMintingActivitiesResponse) Reset() {
*x = GetMintingActivitiesResponse{}
mi := &file_activity_proto_msgTypes[15]
mi := &file_activity_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1308,7 +1584,7 @@ func (x *GetMintingActivitiesResponse) String() string {
func (*GetMintingActivitiesResponse) ProtoMessage() {}
func (x *GetMintingActivitiesResponse) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[15]
mi := &file_activity_proto_msgTypes[19]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1321,7 +1597,7 @@ func (x *GetMintingActivitiesResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetMintingActivitiesResponse.ProtoReflect.Descriptor instead.
func (*GetMintingActivitiesResponse) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{15}
return file_activity_proto_rawDescGZIP(), []int{19}
}
func (x *GetMintingActivitiesResponse) GetBase() *common.BaseResponse {
@ -1372,7 +1648,7 @@ type GetLatestContributionsRequest struct {
func (x *GetLatestContributionsRequest) Reset() {
*x = GetLatestContributionsRequest{}
mi := &file_activity_proto_msgTypes[16]
mi := &file_activity_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1384,7 +1660,7 @@ func (x *GetLatestContributionsRequest) String() string {
func (*GetLatestContributionsRequest) ProtoMessage() {}
func (x *GetLatestContributionsRequest) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[16]
mi := &file_activity_proto_msgTypes[20]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1397,7 +1673,7 @@ func (x *GetLatestContributionsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetLatestContributionsRequest.ProtoReflect.Descriptor instead.
func (*GetLatestContributionsRequest) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{16}
return file_activity_proto_rawDescGZIP(), []int{20}
}
func (x *GetLatestContributionsRequest) GetActivityId() int64 {
@ -1449,7 +1725,7 @@ type ContributionRecord struct {
func (x *ContributionRecord) Reset() {
*x = ContributionRecord{}
mi := &file_activity_proto_msgTypes[17]
mi := &file_activity_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1461,7 +1737,7 @@ func (x *ContributionRecord) String() string {
func (*ContributionRecord) ProtoMessage() {}
func (x *ContributionRecord) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[17]
mi := &file_activity_proto_msgTypes[21]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1474,7 +1750,7 @@ func (x *ContributionRecord) ProtoReflect() protoreflect.Message {
// Deprecated: Use ContributionRecord.ProtoReflect.Descriptor instead.
func (*ContributionRecord) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{17}
return file_activity_proto_rawDescGZIP(), []int{21}
}
func (x *ContributionRecord) GetId() int64 {
@ -1572,7 +1848,7 @@ type GetLatestContributionsResponse struct {
func (x *GetLatestContributionsResponse) Reset() {
*x = GetLatestContributionsResponse{}
mi := &file_activity_proto_msgTypes[18]
mi := &file_activity_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1584,7 +1860,7 @@ func (x *GetLatestContributionsResponse) String() string {
func (*GetLatestContributionsResponse) ProtoMessage() {}
func (x *GetLatestContributionsResponse) ProtoReflect() protoreflect.Message {
mi := &file_activity_proto_msgTypes[18]
mi := &file_activity_proto_msgTypes[22]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1597,7 +1873,7 @@ func (x *GetLatestContributionsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetLatestContributionsResponse.ProtoReflect.Descriptor instead.
func (*GetLatestContributionsResponse) Descriptor() ([]byte, []int) {
return file_activity_proto_rawDescGZIP(), []int{18}
return file_activity_proto_rawDescGZIP(), []int{22}
}
func (x *GetLatestContributionsResponse) GetBase() *common.BaseResponse {
@ -1663,7 +1939,29 @@ const file_activity_proto_rawDesc = "" +
"\x13total_crystal_spent\x18\x02 \x01(\x03R\x11totalCrystalSpent\x12-\n" +
"\x12total_contribution\x18\x03 \x01(\x03R\x11totalContribution\x12)\n" +
"\x10current_progress\x18\x04 \x01(\x03R\x0fcurrentProgress\x12+\n" +
"\x11remaining_balance\x18\x05 \x01(\x03R\x10remainingBalance\"\xa0\x01\n" +
"\x11remaining_balance\x18\x05 \x01(\x03R\x10remainingBalance\"G\n" +
"\fPurchaseItem\x12\x1b\n" +
"\titem_type\x18\x01 \x01(\tR\bitemType\x12\x1a\n" +
"\bquantity\x18\x02 \x01(\x05R\bquantity\"\xa3\x01\n" +
"\x18BatchPurchaseItemRequest\x12\x1f\n" +
"\vactivity_id\x18\x01 \x01(\x03R\n" +
"activityId\x124\n" +
"\x05items\x18\x02 \x03(\v2\x1e.topfans.activity.PurchaseItemR\x05items\x12\x17\n" +
"\astar_id\x18\x03 \x01(\x03R\x06starId\x12\x17\n" +
"\auser_id\x18\x04 \x01(\x03R\x06userId\"\x82\x03\n" +
"\x19BatchPurchaseItemResponse\x120\n" +
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12.\n" +
"\x13total_crystal_spent\x18\x02 \x01(\x03R\x11totalCrystalSpent\x12-\n" +
"\x12total_contribution\x18\x03 \x01(\x03R\x11totalContribution\x12)\n" +
"\x10current_progress\x18\x04 \x01(\x03R\x0fcurrentProgress\x12+\n" +
"\x11remaining_balance\x18\x05 \x01(\x03R\x10remainingBalance\x12#\n" +
"\rsuccess_count\x18\x06 \x01(\x05R\fsuccessCount\x12\x1d\n" +
"\n" +
"fail_count\x18\a \x01(\x05R\tfailCount\x128\n" +
"\x05fails\x18\b \x03(\v2\".topfans.activity.PurchaseFailItemR\x05fails\"G\n" +
"\x10PurchaseFailItem\x12\x1b\n" +
"\titem_type\x18\x01 \x01(\tR\bitemType\x12\x16\n" +
"\x06reason\x18\x02 \x01(\tR\x06reason\"\xa0\x01\n" +
"\x1aContributionRankingRequest\x12\x1f\n" +
"\vactivity_id\x18\x01 \x01(\x03R\n" +
"activityId\x12\x17\n" +
@ -1769,13 +2067,15 @@ const file_activity_proto_rawDesc = "" +
"created_at\x18\f \x01(\x03R\tcreatedAt\"\x92\x01\n" +
"\x1eGetLatestContributionsResponse\x120\n" +
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12>\n" +
"\arecords\x18\x02 \x03(\v2$.topfans.activity.ContributionRecordR\arecords2\xce\t\n" +
"\arecords\x18\x02 \x03(\v2$.topfans.activity.ContributionRecordR\arecords2\xf9\n" +
"\n" +
"\x0fActivityService\x12\x82\x01\n" +
"\x0fGetActivityList\x12(.topfans.activity.GetActivityListRequest\x1a).topfans.activity.GetActivityListResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\x12\x12/api/v1/activities\x12y\n" +
"\vGetActivity\x12$.topfans.activity.GetProgressRequest\x1a\x1a.topfans.activity.Activity\"(\x82\xd3\xe4\x93\x02\"\x12 /api/v1/activities/{activity_id}\x12\x91\x01\n" +
"\x10GetActivityItems\x12$.topfans.activity.GetProgressRequest\x1a'.topfans.activity.ActivityItemsResponse\".\x82\xd3\xe4\x93\x02(\x12&/api/v1/activities/{activity_id}/items\x12\x8d\x01\n" +
"\vGetProgress\x12$.topfans.activity.GetProgressRequest\x1a%.topfans.activity.GetProgressResponse\"1\x82\xd3\xe4\x93\x02+\x12)/api/v1/activities/{activity_id}/progress\x12\x93\x01\n" +
"\fPurchaseItem\x12%.topfans.activity.PurchaseItemRequest\x1a&.topfans.activity.PurchaseItemResponse\"4\x82\xd3\xe4\x93\x02.:\x01*\")/api/v1/activities/{activity_id}/purchase\x12\xa7\x01\n" +
"\fPurchaseItem\x12%.topfans.activity.PurchaseItemRequest\x1a&.topfans.activity.PurchaseItemResponse\"4\x82\xd3\xe4\x93\x02.:\x01*\")/api/v1/activities/{activity_id}/purchase\x12\xa8\x01\n" +
"\x11BatchPurchaseItem\x12*.topfans.activity.BatchPurchaseItemRequest\x1a+.topfans.activity.BatchPurchaseItemResponse\":\x82\xd3\xe4\x93\x024:\x01*\"//api/v1/activities/{activity_id}/batch-purchase\x12\xa7\x01\n" +
"\x16GetContributionRanking\x12,.topfans.activity.ContributionRankingRequest\x1a-.topfans.activity.ContributionRankingResponse\"0\x82\xd3\xe4\x93\x02*\x12(/api/v1/activities/{activity_id}/ranking\x12\x99\x01\n" +
"\x14GetMintingActivities\x12-.topfans.activity.GetMintingActivitiesRequest\x1a..topfans.activity.GetMintingActivitiesResponse\"\"\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/v1/minting-activities\x12\xba\x01\n" +
"\x16GetLatestContributions\x12/.topfans.activity.GetLatestContributionsRequest\x1a0.topfans.activity.GetLatestContributionsResponse\"=\x82\xd3\xe4\x93\x027\x125/api/v1/activities/{activity_id}/contributions/latestB8Z6github.com/topfans/backend/pkg/proto/activity;activityb\x06proto3"
@ -1792,64 +2092,73 @@ func file_activity_proto_rawDescGZIP() []byte {
return file_activity_proto_rawDescData
}
var file_activity_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_activity_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
var file_activity_proto_goTypes = []any{
(*Activity)(nil), // 0: topfans.activity.Activity
(*ActivityItem)(nil), // 1: topfans.activity.ActivityItem
(*ActivityItemsResponse)(nil), // 2: topfans.activity.ActivityItemsResponse
(*PurchaseItemRequest)(nil), // 3: topfans.activity.PurchaseItemRequest
(*PurchaseItemResponse)(nil), // 4: topfans.activity.PurchaseItemResponse
(*ContributionRankingRequest)(nil), // 5: topfans.activity.ContributionRankingRequest
(*ContributionRankingItem)(nil), // 6: topfans.activity.ContributionRankingItem
(*ContributionRankingResponse)(nil), // 7: topfans.activity.ContributionRankingResponse
(*MyContribution)(nil), // 8: topfans.activity.MyContribution
(*GetActivityListRequest)(nil), // 9: topfans.activity.GetActivityListRequest
(*GetActivityListResponse)(nil), // 10: topfans.activity.GetActivityListResponse
(*GetProgressRequest)(nil), // 11: topfans.activity.GetProgressRequest
(*GetProgressResponse)(nil), // 12: topfans.activity.GetProgressResponse
(*MintingActivity)(nil), // 13: topfans.activity.MintingActivity
(*GetMintingActivitiesRequest)(nil), // 14: topfans.activity.GetMintingActivitiesRequest
(*GetMintingActivitiesResponse)(nil), // 15: topfans.activity.GetMintingActivitiesResponse
(*GetLatestContributionsRequest)(nil), // 16: topfans.activity.GetLatestContributionsRequest
(*ContributionRecord)(nil), // 17: topfans.activity.ContributionRecord
(*GetLatestContributionsResponse)(nil), // 18: topfans.activity.GetLatestContributionsResponse
(*common.BaseResponse)(nil), // 19: topfans.common.BaseResponse
(*PurchaseItem)(nil), // 5: topfans.activity.PurchaseItem
(*BatchPurchaseItemRequest)(nil), // 6: topfans.activity.BatchPurchaseItemRequest
(*BatchPurchaseItemResponse)(nil), // 7: topfans.activity.BatchPurchaseItemResponse
(*PurchaseFailItem)(nil), // 8: topfans.activity.PurchaseFailItem
(*ContributionRankingRequest)(nil), // 9: topfans.activity.ContributionRankingRequest
(*ContributionRankingItem)(nil), // 10: topfans.activity.ContributionRankingItem
(*ContributionRankingResponse)(nil), // 11: topfans.activity.ContributionRankingResponse
(*MyContribution)(nil), // 12: topfans.activity.MyContribution
(*GetActivityListRequest)(nil), // 13: topfans.activity.GetActivityListRequest
(*GetActivityListResponse)(nil), // 14: topfans.activity.GetActivityListResponse
(*GetProgressRequest)(nil), // 15: topfans.activity.GetProgressRequest
(*GetProgressResponse)(nil), // 16: topfans.activity.GetProgressResponse
(*MintingActivity)(nil), // 17: topfans.activity.MintingActivity
(*GetMintingActivitiesRequest)(nil), // 18: topfans.activity.GetMintingActivitiesRequest
(*GetMintingActivitiesResponse)(nil), // 19: topfans.activity.GetMintingActivitiesResponse
(*GetLatestContributionsRequest)(nil), // 20: topfans.activity.GetLatestContributionsRequest
(*ContributionRecord)(nil), // 21: topfans.activity.ContributionRecord
(*GetLatestContributionsResponse)(nil), // 22: topfans.activity.GetLatestContributionsResponse
(*common.BaseResponse)(nil), // 23: topfans.common.BaseResponse
}
var file_activity_proto_depIdxs = []int32{
1, // 0: topfans.activity.Activity.items:type_name -> topfans.activity.ActivityItem
1, // 1: topfans.activity.ActivityItemsResponse.items:type_name -> topfans.activity.ActivityItem
19, // 2: topfans.activity.PurchaseItemResponse.base:type_name -> topfans.common.BaseResponse
19, // 3: topfans.activity.ContributionRankingResponse.base:type_name -> topfans.common.BaseResponse
6, // 4: topfans.activity.ContributionRankingResponse.items:type_name -> topfans.activity.ContributionRankingItem
8, // 5: topfans.activity.ContributionRankingResponse.my_contribution:type_name -> topfans.activity.MyContribution
19, // 6: topfans.activity.GetActivityListResponse.base:type_name -> topfans.common.BaseResponse
0, // 7: topfans.activity.GetActivityListResponse.activities:type_name -> topfans.activity.Activity
19, // 8: topfans.activity.GetProgressResponse.base:type_name -> topfans.common.BaseResponse
19, // 9: topfans.activity.GetMintingActivitiesResponse.base:type_name -> topfans.common.BaseResponse
13, // 10: topfans.activity.GetMintingActivitiesResponse.activities:type_name -> topfans.activity.MintingActivity
19, // 11: topfans.activity.GetLatestContributionsResponse.base:type_name -> topfans.common.BaseResponse
17, // 12: topfans.activity.GetLatestContributionsResponse.records:type_name -> topfans.activity.ContributionRecord
9, // 13: topfans.activity.ActivityService.GetActivityList:input_type -> topfans.activity.GetActivityListRequest
11, // 14: topfans.activity.ActivityService.GetActivity:input_type -> topfans.activity.GetProgressRequest
11, // 15: topfans.activity.ActivityService.GetActivityItems:input_type -> topfans.activity.GetProgressRequest
11, // 16: topfans.activity.ActivityService.GetProgress:input_type -> topfans.activity.GetProgressRequest
3, // 17: topfans.activity.ActivityService.PurchaseItem:input_type -> topfans.activity.PurchaseItemRequest
5, // 18: topfans.activity.ActivityService.GetContributionRanking:input_type -> topfans.activity.ContributionRankingRequest
14, // 19: topfans.activity.ActivityService.GetMintingActivities:input_type -> topfans.activity.GetMintingActivitiesRequest
16, // 20: topfans.activity.ActivityService.GetLatestContributions:input_type -> topfans.activity.GetLatestContributionsRequest
10, // 21: topfans.activity.ActivityService.GetActivityList:output_type -> topfans.activity.GetActivityListResponse
0, // 22: topfans.activity.ActivityService.GetActivity:output_type -> topfans.activity.Activity
2, // 23: topfans.activity.ActivityService.GetActivityItems:output_type -> topfans.activity.ActivityItemsResponse
12, // 24: topfans.activity.ActivityService.GetProgress:output_type -> topfans.activity.GetProgressResponse
4, // 25: topfans.activity.ActivityService.PurchaseItem:output_type -> topfans.activity.PurchaseItemResponse
7, // 26: topfans.activity.ActivityService.GetContributionRanking:output_type -> topfans.activity.ContributionRankingResponse
15, // 27: topfans.activity.ActivityService.GetMintingActivities:output_type -> topfans.activity.GetMintingActivitiesResponse
18, // 28: topfans.activity.ActivityService.GetLatestContributions:output_type -> topfans.activity.GetLatestContributionsResponse
21, // [21:29] is the sub-list for method output_type
13, // [13:21] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
13, // [13:13] is the sub-list for extension extendee
0, // [0:13] is the sub-list for field type_name
23, // 2: topfans.activity.PurchaseItemResponse.base:type_name -> topfans.common.BaseResponse
5, // 3: topfans.activity.BatchPurchaseItemRequest.items:type_name -> topfans.activity.PurchaseItem
23, // 4: topfans.activity.BatchPurchaseItemResponse.base:type_name -> topfans.common.BaseResponse
8, // 5: topfans.activity.BatchPurchaseItemResponse.fails:type_name -> topfans.activity.PurchaseFailItem
23, // 6: topfans.activity.ContributionRankingResponse.base:type_name -> topfans.common.BaseResponse
10, // 7: topfans.activity.ContributionRankingResponse.items:type_name -> topfans.activity.ContributionRankingItem
12, // 8: topfans.activity.ContributionRankingResponse.my_contribution:type_name -> topfans.activity.MyContribution
23, // 9: topfans.activity.GetActivityListResponse.base:type_name -> topfans.common.BaseResponse
0, // 10: topfans.activity.GetActivityListResponse.activities:type_name -> topfans.activity.Activity
23, // 11: topfans.activity.GetProgressResponse.base:type_name -> topfans.common.BaseResponse
23, // 12: topfans.activity.GetMintingActivitiesResponse.base:type_name -> topfans.common.BaseResponse
17, // 13: topfans.activity.GetMintingActivitiesResponse.activities:type_name -> topfans.activity.MintingActivity
23, // 14: topfans.activity.GetLatestContributionsResponse.base:type_name -> topfans.common.BaseResponse
21, // 15: topfans.activity.GetLatestContributionsResponse.records:type_name -> topfans.activity.ContributionRecord
13, // 16: topfans.activity.ActivityService.GetActivityList:input_type -> topfans.activity.GetActivityListRequest
15, // 17: topfans.activity.ActivityService.GetActivity:input_type -> topfans.activity.GetProgressRequest
15, // 18: topfans.activity.ActivityService.GetActivityItems:input_type -> topfans.activity.GetProgressRequest
15, // 19: topfans.activity.ActivityService.GetProgress:input_type -> topfans.activity.GetProgressRequest
3, // 20: topfans.activity.ActivityService.PurchaseItem:input_type -> topfans.activity.PurchaseItemRequest
6, // 21: topfans.activity.ActivityService.BatchPurchaseItem:input_type -> topfans.activity.BatchPurchaseItemRequest
9, // 22: topfans.activity.ActivityService.GetContributionRanking:input_type -> topfans.activity.ContributionRankingRequest
18, // 23: topfans.activity.ActivityService.GetMintingActivities:input_type -> topfans.activity.GetMintingActivitiesRequest
20, // 24: topfans.activity.ActivityService.GetLatestContributions:input_type -> topfans.activity.GetLatestContributionsRequest
14, // 25: topfans.activity.ActivityService.GetActivityList:output_type -> topfans.activity.GetActivityListResponse
0, // 26: topfans.activity.ActivityService.GetActivity:output_type -> topfans.activity.Activity
2, // 27: topfans.activity.ActivityService.GetActivityItems:output_type -> topfans.activity.ActivityItemsResponse
16, // 28: topfans.activity.ActivityService.GetProgress:output_type -> topfans.activity.GetProgressResponse
4, // 29: topfans.activity.ActivityService.PurchaseItem:output_type -> topfans.activity.PurchaseItemResponse
7, // 30: topfans.activity.ActivityService.BatchPurchaseItem:output_type -> topfans.activity.BatchPurchaseItemResponse
11, // 31: topfans.activity.ActivityService.GetContributionRanking:output_type -> topfans.activity.ContributionRankingResponse
19, // 32: topfans.activity.ActivityService.GetMintingActivities:output_type -> topfans.activity.GetMintingActivitiesResponse
22, // 33: topfans.activity.ActivityService.GetLatestContributions:output_type -> topfans.activity.GetLatestContributionsResponse
25, // [25:34] is the sub-list for method output_type
16, // [16:25] is the sub-list for method input_type
16, // [16:16] is the sub-list for extension type_name
16, // [16:16] is the sub-list for extension extendee
0, // [0:16] is the sub-list for field type_name
}
func init() { file_activity_proto_init() }
@ -1863,7 +2172,7 @@ func file_activity_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_activity_proto_rawDesc), len(file_activity_proto_rawDesc)),
NumEnums: 0,
NumMessages: 19,
NumMessages: 23,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -46,6 +46,8 @@ const (
ActivityServiceGetProgressProcedure = "/topfans.activity.ActivityService/GetProgress"
// ActivityServicePurchaseItemProcedure is the fully-qualified name of the ActivityService's PurchaseItem RPC.
ActivityServicePurchaseItemProcedure = "/topfans.activity.ActivityService/PurchaseItem"
// ActivityServiceBatchPurchaseItemProcedure is the fully-qualified name of the ActivityService's BatchPurchaseItem RPC.
ActivityServiceBatchPurchaseItemProcedure = "/topfans.activity.ActivityService/BatchPurchaseItem"
// ActivityServiceGetContributionRankingProcedure is the fully-qualified name of the ActivityService's GetContributionRanking RPC.
ActivityServiceGetContributionRankingProcedure = "/topfans.activity.ActivityService/GetContributionRanking"
// ActivityServiceGetMintingActivitiesProcedure is the fully-qualified name of the ActivityService's GetMintingActivities RPC.
@ -65,6 +67,7 @@ type ActivityService interface {
GetActivityItems(ctx context.Context, req *GetProgressRequest, opts ...client.CallOption) (*ActivityItemsResponse, error)
GetProgress(ctx context.Context, req *GetProgressRequest, opts ...client.CallOption) (*GetProgressResponse, error)
PurchaseItem(ctx context.Context, req *PurchaseItemRequest, opts ...client.CallOption) (*PurchaseItemResponse, error)
BatchPurchaseItem(ctx context.Context, req *BatchPurchaseItemRequest, opts ...client.CallOption) (*BatchPurchaseItemResponse, error)
GetContributionRanking(ctx context.Context, req *ContributionRankingRequest, opts ...client.CallOption) (*ContributionRankingResponse, error)
GetMintingActivities(ctx context.Context, req *GetMintingActivitiesRequest, opts ...client.CallOption) (*GetMintingActivitiesResponse, error)
GetLatestContributions(ctx context.Context, req *GetLatestContributionsRequest, opts ...client.CallOption) (*GetLatestContributionsResponse, error)
@ -130,6 +133,14 @@ func (c *ActivityServiceImpl) PurchaseItem(ctx context.Context, req *PurchaseIte
return resp, nil
}
func (c *ActivityServiceImpl) BatchPurchaseItem(ctx context.Context, req *BatchPurchaseItemRequest, opts ...client.CallOption) (*BatchPurchaseItemResponse, error) {
resp := new(BatchPurchaseItemResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "BatchPurchaseItem", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *ActivityServiceImpl) GetContributionRanking(ctx context.Context, req *ContributionRankingRequest, opts ...client.CallOption) (*ContributionRankingResponse, error) {
resp := new(ContributionRankingResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetContributionRanking", opts...); err != nil {
@ -156,7 +167,7 @@ func (c *ActivityServiceImpl) GetLatestContributions(ctx context.Context, req *G
var ActivityService_ClientInfo = client.ClientInfo{
InterfaceName: "topfans.activity.ActivityService",
MethodNames: []string{"GetActivityList", "GetActivity", "GetActivityItems", "GetProgress", "PurchaseItem", "GetContributionRanking", "GetMintingActivities", "GetLatestContributions"},
MethodNames: []string{"GetActivityList", "GetActivity", "GetActivityItems", "GetProgress", "PurchaseItem", "BatchPurchaseItem", "GetContributionRanking", "GetMintingActivities", "GetLatestContributions"},
ConnectionInjectFunc: func(dubboCliRaw interface{}, conn *client.Connection) {
dubboCli := dubboCliRaw.(*ActivityServiceImpl)
dubboCli.conn = conn
@ -170,6 +181,7 @@ type ActivityServiceHandler interface {
GetActivityItems(context.Context, *GetProgressRequest) (*ActivityItemsResponse, error)
GetProgress(context.Context, *GetProgressRequest) (*GetProgressResponse, error)
PurchaseItem(context.Context, *PurchaseItemRequest) (*PurchaseItemResponse, error)
BatchPurchaseItem(context.Context, *BatchPurchaseItemRequest) (*BatchPurchaseItemResponse, error)
GetContributionRanking(context.Context, *ContributionRankingRequest) (*ContributionRankingResponse, error)
GetMintingActivities(context.Context, *GetMintingActivitiesRequest) (*GetMintingActivitiesResponse, error)
GetLatestContributions(context.Context, *GetLatestContributionsRequest) (*GetLatestContributionsResponse, error)
@ -262,6 +274,21 @@ var ActivityService_ServiceInfo = server.ServiceInfo{
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "BatchPurchaseItem",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(BatchPurchaseItemRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*BatchPurchaseItemRequest)
res, err := handler.(ActivityServiceHandler).BatchPurchaseItem(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetContributionRanking",
Type: constant.CallUnary,

View File

@ -951,7 +951,7 @@ type EstimateMintCostResponse struct {
CurrentBalance int64 `protobuf:"varint,3,opt,name=current_balance,json=currentBalance,proto3" json:"current_balance,omitempty"` // 当前余额(铸造前)
BalanceAfter int64 `protobuf:"varint,4,opt,name=balance_after,json=balanceAfter,proto3" json:"balance_after,omitempty"` // 铸造后余额
MintCount int32 `protobuf:"varint,5,opt,name=mint_count,json=mintCount,proto3" json:"mint_count,omitempty"` // 本次是第几次铸造
NextTierHint string `protobuf:"bytes,6,opt,name=next_tier_hint,json=nextTierHint,proto3" json:"next_tier_hint,omitempty"` // 下一阶梯提示
NextTierCost int64 `protobuf:"varint,6,opt,name=next_tier_cost,json=nextTierCost,proto3" json:"next_tier_cost,omitempty"` // 下一阶梯费用
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -1021,11 +1021,11 @@ func (x *EstimateMintCostResponse) GetMintCount() int32 {
return 0
}
func (x *EstimateMintCostResponse) GetNextTierHint() string {
func (x *EstimateMintCostResponse) GetNextTierCost() int64 {
if x != nil {
return x.NextTierHint
return x.NextTierCost
}
return ""
return 0
}
// 获取我的藏品列表请求
@ -3736,7 +3736,7 @@ const file_asset_proto_rawDesc = "" +
"\rbalance_after\x18\x04 \x01(\x03R\fbalanceAfter\x12\x1d\n" +
"\n" +
"mint_count\x18\x05 \x01(\x05R\tmintCount\x12$\n" +
"\x0enext_tier_hint\x18\x06 \x01(\tR\fnextTierHint\"\x8b\x01\n" +
"\x0enext_tier_cost\x18\x06 \x01(\x03R\fnextTierCost\"\x8b\x01\n" +
"\x12GetMyAssetsRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x1b\n" +
"\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x16\n" +

View File

@ -42,6 +42,8 @@ const (
AssetServicePreCreateMintOrderProcedure = "/topfans.asset.AssetService/PreCreateMintOrder"
// AssetServiceCreateMintOrderProcedure is the fully-qualified name of the AssetService's CreateMintOrder RPC.
AssetServiceCreateMintOrderProcedure = "/topfans.asset.AssetService/CreateMintOrder"
// AssetServiceEstimateMintCostProcedure is the fully-qualified name of the AssetService's EstimateMintCost RPC.
AssetServiceEstimateMintCostProcedure = "/topfans.asset.AssetService/EstimateMintCost"
// AssetServiceGetMyAssetsProcedure is the fully-qualified name of the AssetService's GetMyAssets RPC.
AssetServiceGetMyAssetsProcedure = "/topfans.asset.AssetService/GetMyAssets"
// AssetServiceGetAssetProcedure is the fully-qualified name of the AssetService's GetAsset RPC.
@ -64,6 +66,16 @@ const (
AssetServiceGetAssetLikesProcedure = "/topfans.asset.AssetService/GetAssetLikes"
// AssetServiceClearAssetLikeRecordsProcedure is the fully-qualified name of the AssetService's ClearAssetLikeRecords RPC.
AssetServiceClearAssetLikeRecordsProcedure = "/topfans.asset.AssetService/ClearAssetLikeRecords"
// AssetServiceUploadMaterialProcedure is the fully-qualified name of the AssetService's UploadMaterial RPC.
AssetServiceUploadMaterialProcedure = "/topfans.asset.AssetService/UploadMaterial"
// AssetServiceBindAssetMaterialsProcedure is the fully-qualified name of the AssetService's BindAssetMaterials RPC.
AssetServiceBindAssetMaterialsProcedure = "/topfans.asset.AssetService/BindAssetMaterials"
// AssetServiceGetAssetMaterialsProcedure is the fully-qualified name of the AssetService's GetAssetMaterials RPC.
AssetServiceGetAssetMaterialsProcedure = "/topfans.asset.AssetService/GetAssetMaterials"
// AssetServiceUpdateMaterialLayerOrderProcedure is the fully-qualified name of the AssetService's UpdateMaterialLayerOrder RPC.
AssetServiceUpdateMaterialLayerOrderProcedure = "/topfans.asset.AssetService/UpdateMaterialLayerOrder"
// AssetServiceUnbindAssetMaterialProcedure is the fully-qualified name of the AssetService's UnbindAssetMaterial RPC.
AssetServiceUnbindAssetMaterialProcedure = "/topfans.asset.AssetService/UnbindAssetMaterial"
)
var (
@ -75,6 +87,7 @@ type AssetService interface {
InitMintOrder(ctx context.Context, req *InitMintOrderRequest, opts ...client.CallOption) (*InitMintOrderResponse, error)
PreCreateMintOrder(ctx context.Context, req *PreCreateMintOrderRequest, opts ...client.CallOption) (*PreCreateMintOrderResponse, error)
CreateMintOrder(ctx context.Context, req *CreateMintOrderRequest, opts ...client.CallOption) (*CreateMintOrderResponse, error)
EstimateMintCost(ctx context.Context, req *EstimateMintCostRequest, opts ...client.CallOption) (*EstimateMintCostResponse, error)
GetMyAssets(ctx context.Context, req *GetMyAssetsRequest, opts ...client.CallOption) (*GetMyAssetsResponse, error)
GetAsset(ctx context.Context, req *GetAssetRequest, opts ...client.CallOption) (*GetAssetResponse, error)
GetAssetStatus(ctx context.Context, req *GetAssetStatusRequest, opts ...client.CallOption) (*GetAssetStatusResponse, error)
@ -91,7 +104,6 @@ type AssetService interface {
GetAssetMaterials(ctx context.Context, req *GetAssetMaterialsRequest, opts ...client.CallOption) (*GetAssetMaterialsResponse, error)
UpdateMaterialLayerOrder(ctx context.Context, req *UpdateMaterialLayerOrderRequest, opts ...client.CallOption) (*UpdateMaterialLayerOrderResponse, error)
UnbindAssetMaterial(ctx context.Context, req *UnbindAssetMaterialRequest, opts ...client.CallOption) (*UnbindAssetMaterialResponse, error)
EstimateMintCost(ctx context.Context, req *EstimateMintCostRequest, opts ...client.CallOption) (*EstimateMintCostResponse, error)
}
// NewAssetService constructs a client for the asset.AssetService service.
@ -138,6 +150,14 @@ func (c *AssetServiceImpl) CreateMintOrder(ctx context.Context, req *CreateMintO
return resp, nil
}
func (c *AssetServiceImpl) EstimateMintCost(ctx context.Context, req *EstimateMintCostRequest, opts ...client.CallOption) (*EstimateMintCostResponse, error) {
resp := new(EstimateMintCostResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "EstimateMintCost", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *AssetServiceImpl) GetMyAssets(ctx context.Context, req *GetMyAssetsRequest, opts ...client.CallOption) (*GetMyAssetsResponse, error) {
resp := new(GetMyAssetsResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetMyAssets", opts...); err != nil {
@ -266,17 +286,9 @@ func (c *AssetServiceImpl) UnbindAssetMaterial(ctx context.Context, req *UnbindA
return resp, nil
}
func (c *AssetServiceImpl) EstimateMintCost(ctx context.Context, req *EstimateMintCostRequest, opts ...client.CallOption) (*EstimateMintCostResponse, error) {
resp := new(EstimateMintCostResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "EstimateMintCost", opts...); err != nil {
return nil, err
}
return resp, nil
}
var AssetService_ClientInfo = client.ClientInfo{
InterfaceName: "topfans.asset.AssetService",
MethodNames: []string{"InitMintOrder", "PreCreateMintOrder", "CreateMintOrder", "GetMyAssets", "GetAsset", "GetAssetStatus", "GetMintOrder", "CancelMintOrder", "GetAssetForRPC", "LikeAsset", "UnlikeAsset", "CheckAssetLike", "GetAssetLikes", "ClearAssetLikeRecords", "UploadMaterial", "BindAssetMaterials", "GetAssetMaterials", "UpdateMaterialLayerOrder", "UnbindAssetMaterial", "EstimateMintCost"},
MethodNames: []string{"InitMintOrder", "PreCreateMintOrder", "CreateMintOrder", "EstimateMintCost", "GetMyAssets", "GetAsset", "GetAssetStatus", "GetMintOrder", "CancelMintOrder", "GetAssetForRPC", "LikeAsset", "UnlikeAsset", "CheckAssetLike", "GetAssetLikes", "ClearAssetLikeRecords", "UploadMaterial", "BindAssetMaterials", "GetAssetMaterials", "UpdateMaterialLayerOrder", "UnbindAssetMaterial"},
ConnectionInjectFunc: func(dubboCliRaw interface{}, conn *client.Connection) {
dubboCli := dubboCliRaw.(*AssetServiceImpl)
dubboCli.conn = conn
@ -288,6 +300,7 @@ type AssetServiceHandler interface {
InitMintOrder(context.Context, *InitMintOrderRequest) (*InitMintOrderResponse, error)
PreCreateMintOrder(context.Context, *PreCreateMintOrderRequest) (*PreCreateMintOrderResponse, error)
CreateMintOrder(context.Context, *CreateMintOrderRequest) (*CreateMintOrderResponse, error)
EstimateMintCost(context.Context, *EstimateMintCostRequest) (*EstimateMintCostResponse, error)
GetMyAssets(context.Context, *GetMyAssetsRequest) (*GetMyAssetsResponse, error)
GetAsset(context.Context, *GetAssetRequest) (*GetAssetResponse, error)
GetAssetStatus(context.Context, *GetAssetStatusRequest) (*GetAssetStatusResponse, error)
@ -304,7 +317,6 @@ type AssetServiceHandler interface {
GetAssetMaterials(context.Context, *GetAssetMaterialsRequest) (*GetAssetMaterialsResponse, error)
UpdateMaterialLayerOrder(context.Context, *UpdateMaterialLayerOrderRequest) (*UpdateMaterialLayerOrderResponse, error)
UnbindAssetMaterial(context.Context, *UnbindAssetMaterialRequest) (*UnbindAssetMaterialResponse, error)
EstimateMintCost(context.Context, *EstimateMintCostRequest) (*EstimateMintCostResponse, error)
}
func RegisterAssetServiceHandler(srv *server.Server, hdlr AssetServiceHandler, opts ...server.ServiceOption) error {
@ -364,6 +376,21 @@ var AssetService_ServiceInfo = server.ServiceInfo{
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "EstimateMintCost",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(EstimateMintCostRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*EstimateMintCostRequest)
res, err := handler.(AssetServiceHandler).EstimateMintCost(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetMyAssets",
Type: constant.CallUnary,
@ -604,20 +631,5 @@ var AssetService_ServiceInfo = server.ServiceInfo{
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "EstimateMintCost",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(EstimateMintCostRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*EstimateMintCostRequest)
res, err := handler.(AssetServiceHandler).EstimateMintCost(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
},
}

View File

@ -65,6 +65,38 @@ message PurchaseItemResponse {
int64 remaining_balance = 5; //
}
//
message PurchaseItem {
string item_type = 1;
int32 quantity = 2;
}
//
message BatchPurchaseItemRequest {
int64 activity_id = 1;
repeated PurchaseItem items = 2; //
int64 star_id = 3;
int64 user_id = 4; // ID
}
//
message BatchPurchaseItemResponse {
topfans.common.BaseResponse base = 1;
int64 total_crystal_spent = 2; //
int64 total_contribution = 3; //
int64 current_progress = 4; //
int64 remaining_balance = 5; //
int32 success_count = 6; //
int32 fail_count = 7; //
repeated PurchaseFailItem fails = 8; //
}
//
message PurchaseFailItem {
string item_type = 1;
string reason = 2;
}
//
message ContributionRankingRequest {
int64 activity_id = 1;
@ -237,6 +269,14 @@ service ActivityService {
};
}
//
rpc BatchPurchaseItem(BatchPurchaseItemRequest) returns (BatchPurchaseItemResponse) {
option (google.api.http) = {
post: "/api/v1/activities/{activity_id}/batch-purchase"
body: "*"
};
}
//
rpc GetContributionRanking(ContributionRankingRequest) returns (ContributionRankingResponse) {
option (google.api.http) = {

View File

@ -129,7 +129,7 @@ message EstimateMintCostResponse {
int64 current_balance = 3; //
int64 balance_after = 4; //
int32 mint_count = 5; //
string next_tier_hint = 6; //
int64 next_tier_cost = 6; //
}
// ==================== ====================

View File

@ -51,6 +51,20 @@ fi
echo "✅ 所有必需插件已安装"
echo ""
# 辅助函数:移动 protoc-gen-go-triple v3 生成的文件到正确位置
# v3 插件根据 go_package 路径生成文件到 github.com/ 目录,需要手动移动
move_triple_files() {
local module_path="$1"
local target_dir="$2"
local triple_file=$(basename "$module_path").triple.go
local src_file="github.com/$module_path/$triple_file"
if [ -f "$src_file" ]; then
mv "$src_file" "$target_dir/"
rm -rf "github.com/$module_path"
echo "$triple_file 已移动到正确位置"
fi
}
# 预先创建目标目录
echo "📁 创建目标目录..."
for name in common user social asset gallery ranking activity task starbook; do
@ -77,6 +91,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/user \
--go-triple_opt=paths=source_relative \
user.proto
move_triple_files "topfans/backend/pkg/proto/user" "pkg/proto/user"
echo "✅ user.proto 编译完成"
echo ""
@ -90,6 +105,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/social \
--go-triple_opt=paths=source_relative \
social.proto
move_triple_files "topfans/backend/pkg/proto/social" "pkg/proto/social"
echo "✅ social.proto 编译完成"
echo ""
@ -103,6 +119,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/asset \
--go-triple_opt=paths=source_relative \
asset.proto
move_triple_files "topfans/backend/pkg/proto/asset" "pkg/proto/asset"
echo "✅ asset.proto 编译完成"
echo ""
@ -116,6 +133,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/gallery \
--go-triple_opt=paths=source_relative \
gallery.proto
move_triple_files "topfans/backend/pkg/proto/gallery" "pkg/proto/gallery"
echo "✅ gallery.proto 编译完成"
echo ""
@ -129,6 +147,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/ranking \
--go-triple_opt=paths=source_relative \
ranking.proto
move_triple_files "topfans/backend/pkg/proto/ranking" "pkg/proto/ranking"
echo "✅ ranking.proto 编译完成"
echo ""
@ -142,6 +161,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/activity \
--go-triple_opt=paths=source_relative \
activity.proto
move_triple_files "topfans/backend/pkg/proto/activity" "pkg/proto/activity"
echo "✅ activity.proto 编译完成"
echo ""
@ -155,6 +175,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/task \
--go-triple_opt=paths=source_relative \
task.proto
move_triple_files "topfans/backend/pkg/proto/task" "pkg/proto/task"
echo "✅ task.proto 编译完成"
echo ""
@ -168,6 +189,7 @@ protoc --proto_path=proto \
--go-triple_out=pkg/proto/starbook \
--go-triple_opt=paths=source_relative \
starbook.proto
move_triple_files "topfans/backend/pkg/proto/starbook" "pkg/proto/starbook"
echo "✅ starbook.proto 编译完成"
echo ""

View File

@ -162,6 +162,38 @@ func (p *ActivityProvider) PurchaseItem(ctx context.Context, req *pb.PurchaseIte
return resp, nil
}
// BatchPurchaseItem 批量购买道具
func (p *ActivityProvider) BatchPurchaseItem(ctx context.Context, req *pb.BatchPurchaseItemRequest) (*pb.BatchPurchaseItemResponse, error) {
logger.Logger.Info("Received BatchPurchaseItem request",
zap.Int64("activity_id", req.ActivityId),
zap.Int("items_count", len(req.Items)),
zap.Int64("star_id", req.StarId),
)
// 调用Service层
resp, err := p.activityService.BatchPurchaseItem(ctx, req)
if err != nil {
logger.Logger.Error("BatchPurchaseItem failed", zap.Error(err))
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: time.Now().UnixMilli(),
},
}, err
}
logger.Logger.Info("BatchPurchaseItem successful",
zap.Int64("total_crystal_spent", resp.TotalCrystalSpent),
zap.Int64("total_contribution", resp.TotalContribution),
zap.Int64("remaining_balance", resp.RemainingBalance),
zap.Int32("success_count", resp.SuccessCount),
zap.Int32("fail_count", resp.FailCount),
)
return resp, nil
}
// GetContributionRanking 获取贡献点排名
func (p *ActivityProvider) GetContributionRanking(ctx context.Context, req *pb.ContributionRankingRequest) (*pb.ContributionRankingResponse, error) {
logger.Logger.Info("Received GetContributionRanking request",

View File

@ -32,6 +32,9 @@ type ActivityService interface {
// PurchaseItem 购买道具
PurchaseItem(ctx context.Context, req *pb.PurchaseItemRequest) (*pb.PurchaseItemResponse, error)
// BatchPurchaseItem 批量购买道具
BatchPurchaseItem(ctx context.Context, req *pb.BatchPurchaseItemRequest) (*pb.BatchPurchaseItemResponse, error)
// GetContributionRanking 获取贡献点排名
GetContributionRanking(ctx context.Context, req *pb.ContributionRankingRequest) (*pb.ContributionRankingResponse, error)
@ -411,6 +414,222 @@ func (s *activityService) PurchaseItem(ctx context.Context, req *pb.PurchaseItem
}, nil
}
// BatchPurchaseItem 批量购买道具
func (s *activityService) BatchPurchaseItem(ctx context.Context, req *pb.BatchPurchaseItemRequest) (*pb.BatchPurchaseItemResponse, error) {
logger.Logger.Info("BatchPurchaseItem request",
zap.Int64("activity_id", req.ActivityId),
zap.Int("items_count", len(req.Items)),
zap.Int64("star_id", req.StarId),
)
// 参数校验
if req.ActivityId <= 0 {
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 400,
Message: "activity_id is required",
},
}, nil
}
if len(req.Items) == 0 {
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 400,
Message: "items 是必填参数",
},
}, nil
}
userID := req.UserId
// 获取活动
activity, err := s.activityRepo.GetActivityByID(req.ActivityId)
if err != nil {
logger.Logger.Error("GetActivity failed", zap.Error(err))
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 500,
Message: "获取活动失败: " + err.Error(),
},
}, nil
}
if activity == nil {
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 404,
Message: "活动不存在",
},
}, nil
}
// 检查活动状态
currentStatus := activity.GetCurrentStatus()
if currentStatus != "active" {
var message string
switch currentStatus {
case "expired":
message = "activity:expired"
case "pending":
message = "activity:pending"
case "completed":
message = "activity:completed"
default:
message = "activity:expired"
}
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: message,
},
}, nil
}
// 通过RPC获取用户当前水晶余额只调用一次
profile, err := s.userRPCClient.GetFanProfile(userID, req.StarId)
if err != nil {
logger.Logger.Error("GetFanProfile failed", zap.Error(err))
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 500,
Message: "获取粉丝档案失败: " + err.Error(),
},
}, nil
}
if profile == nil {
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 404,
Message: "粉丝档案不存在",
},
}, nil
}
// 计算总消费水晶和贡献点(先计算,用于校验余额)
var totalCost int64
var totalContribution int64
itemInfoMap := make(map[string]*models.ActivityItem)
for _, item := range req.Items {
if item.Quantity <= 0 {
continue
}
activityItem, err := s.activityRepo.GetActivityItemByType(req.ActivityId, item.ItemType)
if err != nil || activityItem == nil {
logger.Logger.Warn("GetActivityItemByType failed or item not found",
zap.String("item_type", item.ItemType),
zap.Error(err),
)
continue
}
itemInfoMap[item.ItemType] = activityItem
totalCost += int64(activityItem.CrystalCost) * int64(item.Quantity)
totalContribution += int64(activityItem.ContributionPoints) * int64(item.Quantity)
}
// 检查水晶余额是否足够
if profile.CrystalBalance < totalCost {
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 400,
Message: "水晶余额不足",
},
}, nil
}
// 通过RPC扣减水晶
newBalance, err := s.userRPCClient.UpdateCrystalBalance(userID, req.StarId, -totalCost)
if err != nil {
logger.Logger.Error("UpdateCrystalBalance failed", zap.Error(err))
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 500,
Message: "扣减水晶失败: " + err.Error(),
},
}, nil
}
// 更新活动进度
newProgress := activity.CurrentProgress + totalContribution
if newProgress > activity.TargetProgress {
newProgress = activity.TargetProgress
}
err = s.activityRepo.UpdateActivityProgress(req.ActivityId, newProgress)
if err != nil {
logger.Logger.Error("UpdateActivityProgress failed", zap.Error(err))
}
// 创建贡献记录(合并为一个记录)
now := time.Now().UnixMilli()
contribution := &models.ActivityContribution{
ActivityID: req.ActivityId,
UserID: userID,
StarID: req.StarId,
Quantity: 0, // 批量购买不记录单数量
CrystalSpent: totalCost,
ContributionPoints: totalContribution,
CreatedAt: now,
}
err = s.activityRepo.CreateContribution(contribution)
if err != nil {
logger.Logger.Error("CreateContribution failed", zap.Error(err))
}
// 更新用户统计
stats, _ := s.activityRepo.GetUserStats(req.ActivityId, userID, req.StarId)
if stats == nil {
stats = &models.ActivityUserStats{
ActivityID: req.ActivityId,
UserID: userID,
StarID: req.StarId,
TotalContribution: 0,
TotalCrystalSpent: 0,
TotalItems: 0,
LastContributeAt: now,
CreatedAt: now,
UpdatedAt: now,
}
}
stats.TotalContribution += totalContribution
stats.TotalCrystalSpent += totalCost
stats.TotalItems += len(req.Items)
stats.LastContributeAt = now
stats.UpdatedAt = now
err = s.activityRepo.UpdateUserStats(stats)
if err != nil {
logger.Logger.Error("UpdateUserStats failed", zap.Error(err))
}
logger.Logger.Info("BatchPurchaseItem success",
zap.Int64("user_id", userID),
zap.Int64("total_cost", totalCost),
zap.Int64("total_contribution", totalContribution),
zap.Int64("new_progress", newProgress),
)
return &pb.BatchPurchaseItemResponse{
Base: &pbCommon.BaseResponse{
Code: 200,
Message: "ok",
},
TotalCrystalSpent: totalCost,
TotalContribution: totalContribution,
CurrentProgress: newProgress,
RemainingBalance: newBalance,
SuccessCount: int32(len(req.Items)),
FailCount: 0,
Fails: []*pb.PurchaseFailItem{},
}, nil
}
// GetContributionRanking 获取贡献点排名
func (s *activityService) GetContributionRanking(ctx context.Context, req *pb.ContributionRankingRequest) (*pb.ContributionRankingResponse, error) {
logger.Logger.Info("GetContributionRanking request",

View File

@ -853,6 +853,6 @@ func (p *AssetProvider) EstimateMintCost(ctx context.Context, req *pb.EstimateMi
CurrentBalance: estimate.CurrentBalance,
BalanceAfter: estimate.AfterBalance,
MintCount: estimate.MintCount,
NextTierHint: estimate.NextTierHint,
NextTierCost: estimate.NextTierCost,
}, nil
}

View File

@ -856,13 +856,13 @@ func (s *mintService) EstimateMintCost(userID, starID int64) (*MintCostEstimate,
afterBalance = 0
}
// 下一阶梯费用提示
var nextTierHint string
// 下一阶梯费用
var nextTierCost int64
nextMintCount := currentMintCount + 2 // 下一次铸造的次数
if nextMintCount <= 10 {
nextCost, err := s.GetMintCost(nextMintCount)
if err == nil {
nextTierHint = fmt.Sprintf("下一阶梯 %d 水晶", nextCost.CostCrystal)
nextTierCost = nextCost.CostCrystal
}
}
@ -871,7 +871,7 @@ func (s *mintService) EstimateMintCost(userID, starID int64) (*MintCostEstimate,
CurrentBalance: currentBalance,
AfterBalance: afterBalance,
MintCount: currentMintCount + 1, // 本次将是第几次铸造
NextTierHint: nextTierHint,
NextTierCost: nextTierCost,
}, nil
}
@ -881,7 +881,7 @@ type MintCostEstimate struct {
CurrentBalance int64 // 当前余额(铸造前)
AfterBalance int64 // 铸造后余额
MintCount int32 // 本次铸造是第几次
NextTierHint string // 下一阶梯提示
NextTierCost int64 // 下一阶梯费用
}
// UpdateMintCountAndBoost 更新铸爱次数和收益提升

View File

@ -1,34 +1,43 @@
<template>
<view v-show="visible" class="modal-overlay" :class="{ 'modal-visible': visible }" @tap="handleCancel">
<view class="modal-box" @tap.stop>
<!-- 左上角关闭按钮 -->
<view class="close-btn" @tap="handleCancel">
<image class="close-icon" src="/static/starbookcontent/tuichu.png" mode="aspectFit"></image>
</view>
<!-- 右上角水晶数量 -->
<view class="crystal-badge" v-if="currentBalance">
<image class="topfans-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
<view class="card-income-text-wrap">
<text class="card-income-text">{{ currentBalance }}</text>
</view>
</view>
<!-- 标题 -->
<view v-if="title" class="modal-title">
<text>{{ title }}</text>
</view>
<view v-else class="modal-title">
<!-- <view v-else class="modal-title">
<text class="modal-title-text">铸造确认</text>
</view>
</view> -->
<!-- 铸造费用信息 -->
<view v-if="costCrystal > 0 || currentBalance > 0" class="cost-info">
<view v-if="costCrystal > 0" class="cost-info">
<view class="cost-row">
<text class="cost-label">本次消耗</text>
<text class="cost-value cost-crystal">{{ costCrystal }} 水晶</text>
<view class="cost-num-wrap">
<text class="cost-num">{{ costCrystal }}</text>
<text class="cost-unit"> 水晶</text>
</view>
<view class="cost-row">
<text class="cost-label">当前余额</text>
<text class="cost-value">{{ currentBalance }} 水晶</text>
</view>
<view class="cost-row">
<text class="cost-label">铸造后余额</text>
<text class="cost-value balance-after">{{ balanceAfter }} 水晶</text>
</view>
<view v-if="mintCount > 0" class="cost-row">
<text class="cost-label">铸造次数</text>
<text class="cost-value"> {{ mintCount }} </text>
<view class="cost-num-wrap">
<text class="cost-unit"> </text>
<text class="cost-num">{{ mintCount }}</text>
<text class="cost-unit"> </text>
</view>
<view v-if="nextTierHint" class="next-tier-hint">
<text>{{ nextTierHint }}</text>
</view>
</view>
@ -39,13 +48,17 @@
<!-- 按钮区 -->
<view class="modal-actions">
<view v-if="showCancel" class="btn btn-cancel" @tap="handleCancel">
<text>{{ cancelText }}</text>
</view>
<view class="btn btn-confirm" @tap="handleConfirm">
<text>{{ confirmText }}</text>
</view>
</view>
<!-- 下一阶段消耗提示 -->
<view v-if="nextTierCost > 0" class="next-tier-cost">
<text class="cost-text">下一阶段消耗 </text>
<text class="cost-num">{{ nextTierCost }}</text>
<text class="cost-text"> 水晶</text>
</view>
</view>
</view>
</template>
@ -74,14 +87,6 @@ const props = defineProps({
type: String,
default: '确认'
},
cancelText: {
type: String,
default: '取消'
},
showCancel: {
type: Boolean,
default: true
},
//
costCrystal: {
type: Number,
@ -91,17 +96,13 @@ const props = defineProps({
type: Number,
default: 0
},
balanceAfter: {
type: Number,
default: 0
},
mintCount: {
type: Number,
default: 0
},
nextTierHint: {
type: String,
default: ''
nextTierCost: {
type: Number,
default: 0
}
})
@ -119,7 +120,10 @@ function handleCancel() {
<style scoped>
.modal-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
@ -138,7 +142,7 @@ function handleCancel() {
.modal-box {
width: 560rpx;
/* background: #fff; */
background-image: url('/static/starbookcontent/beijing.png');
background-image: url('/static/icon/confirmbj.png');
background-size: cover;
background-position: center bottom;
border-radius: 24rpx;
@ -147,6 +151,63 @@ function handleCancel() {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.close-btn {
position: absolute;
top: 8rpx;
left: 8rpx;
width: 64rpx;
height: 64rpx;
z-index: 10;
padding: 10rpx;
}
.close-icon {
width: 64rpx;
height: 64rpx;
}
.crystal-badge {
position: absolute;
top: 8rpx;
right: 8rpx;
/* background: rgba(0, 0, 0, 0.5); */
/* border-radius: 20rpx; */
/* padding: 8rpx 16rpx; */
display: flex;
align-items: center;
}
.topfans-icon {
width: 56rpx;
height: 56rpx;
position: relative;
z-index: 2;
margin-right: -16rpx;
left: 20rpx;
/* top: 8rpx */
transform: rotate(-15deg);
}
.card-income-text-wrap {
width: 64rpx;
height: 32rpx;
background-image: url('@/static/square/shuijingzhanshikuang.png');
background-size: 100% 100%;
background-position: center;
display: flex;
align-items: center;
justify-content: center;
padding: 0 16rpx 0 32rpx;
}
.card-income-text {
font-size: 24rpx;
color: #fff;
font-weight: 700;
text-align: center;
}
.modal-title {
@ -157,8 +218,9 @@ function handleCancel() {
}
.cost-info {
width: 100%;
margin-bottom: 32rpx;
width: 480rpx;
/* margin-bottom: 32rpx; */
padding: 80rpx 0 32rpx;
}
.cost-row {
@ -166,18 +228,17 @@ function handleCancel() {
justify-content: space-between;
align-items: center;
padding: 12rpx 0;
/* border-bottom: 1rpx solid rgba(0, 0, 0, 0.05); */
}
.cost-row:last-child {
border-bottom: none;
}
.cost-label {
background-image: url('@/static/rank/activity-support-icon/beijingkuang.png');
background-size: cover;
background-position: center;
padding: 16rpx;
border-radius: 999rpx;
font-size: 28rpx;
color: #fff;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.cost-value {
@ -185,31 +246,37 @@ function handleCancel() {
color: #fff;
font-weight: 500;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.cost-crystal {
color: #fff;
.cost-num {
font-size: 28rpx;
color: #ff9500;
font-weight: bold;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.balance-after {
.cost-num-wrap {
display: flex;
align-items: center;
gap: 4rpx;
}
.cost-unit {
font-size: 28rpx;
color: #fff;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.next-tier-hint {
.next-tier-cost {
margin-top: 16rpx;
padding: 12rpx 16rpx;
background: rgba(255, 107, 157, 0.1);
border-radius: 12rpx;
text-align: center;
}
.next-tier-hint text {
.next-tier-cost .cost-text {
font-size: 24rpx;
color: #FF6B9D;
color: #fff;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.modal-body {
@ -228,7 +295,7 @@ function handleCancel() {
}
.modal-actions {
width: 100%;
width: 224rpx;
display: flex;
gap: 24rpx;
}
@ -249,7 +316,13 @@ function handleCancel() {
}
.btn-confirm {
background: linear-gradient(to right, #F08399, #B94E73);
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
color: #fff;
}
</style>

View File

@ -5,7 +5,7 @@
<!-- 左上角关闭按钮 -->
<view class="close-btn" @click="handleClose">
<text class="back-icon" :style="{ color: backIconColor }"></text>
<text class="nav-back" :style="{ color: backIconColor }"></text>
</view>
<!-- 功能按钮区域 -->
@ -144,13 +144,18 @@ const handleAdd = () => {
/* 关闭按钮 */
.close-btn {
font-size: 48rpx;
font-weight: bold;
}
.nav-back-icon {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-back {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
/* 浮动装饰物 */

View File

@ -23,29 +23,15 @@
</view> -->
<!-- 光栅卡单张工作台预览陀螺仪 -->
<view
class="lenticular-result-wrap"
:class="{ 'cards-visible': isGiftOpened }"
>
<view class="lenticular-result-wrap" :class="{ 'cards-visible': isGiftOpened }">
<view class="lenticular-result-card">
<view class="card-wrapper craft-card-wrapper">
<image
class="card-frame"
src="/static/square/gerenzhongxincangpinkuang.png"
mode="aspectFit"
/>
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFit" />
<view class="craft-lenticular-slot">
<LenticularCard
v-if="lenticularLayers.length > 0"
class="craft-lenticular-card"
:layers="lenticularLayers"
:transforms="layerTransforms"
:gyro-source="gyroSourceLabel"
:skip-built-in-touch="true"
tilt-hint-text="晃动查看"
:shimmer-mid-opacity="0.16"
@simulate="simulate"
/>
<LenticularCard v-if="lenticularLayers.length > 0" class="craft-lenticular-card"
:layers="lenticularLayers" :transforms="layerTransforms" :gyro-source="gyroSourceLabel"
:skip-built-in-touch="true" tilt-hint-text="晃动查看" :shimmer-mid-opacity="0.16"
@simulate="simulate" />
</view>
</view>
</view>
@ -53,27 +39,13 @@
<!-- 礼盒 -->
<view class="gift-box" :class="{ 'gift-opened': isGiftOpened }">
<image
v-if="!isGiftOpened"
class="gift-image"
src="/static/nft/lihe.png"
mode="aspectFit"
/>
<image
v-else
class="gift-image gift-image-opened"
src="/static/nft/lihe_kaiqi.png"
mode="aspectFit"
/>
<image v-if="!isGiftOpened" class="gift-image" src="/static/nft/lihe.png" mode="aspectFit" />
<image v-else class="gift-image gift-image-opened" src="/static/nft/lihe_kaiqi.png" mode="aspectFit" />
</view>
<!-- 底部按钮 -->
<view class="bottom-action" :class="{ 'bottom-action--row': isCraftDetailFlow }">
<view
v-if="isCraftDetailFlow"
class="action-button action-button--secondary"
@tap="handleRegenerate"
>
<view v-if="isCraftDetailFlow" class="action-button action-button--secondary" @tap="handleRegenerate">
<text class="button-text">重新生成</text>
</view>
<view class="action-button" @tap="selectAsset">
@ -82,17 +54,9 @@
</view>
<!-- 铸造确认弹窗 -->
<ConfirmModal
v-if="showConfirmModal"
:visible="showConfirmModal"
:cost-crystal="confirmCostInfo.costCrystal"
:current-balance="confirmCostInfo.currentBalance"
:balance-after="confirmCostInfo.balanceAfter"
:mint-count="confirmCostInfo.mintCount"
:next-tier-hint="confirmCostInfo.nextTierHint"
@confirm="handleConfirmMint"
@cancel="handleCancelMint"
/>
<ConfirmModal v-if="showConfirmModal" :visible="showConfirmModal" :cost-crystal="confirmCostInfo.costCrystal"
:current-balance="confirmCostInfo.currentBalance" :mint-count="confirmCostInfo.mintCount"
:next-tier-cost="confirmCostInfo.nextTierCost" @confirm="handleConfirmMint" @cancel="handleCancelMint" />
</view>
</template>
@ -131,9 +95,8 @@ const showConfirmModal = ref(false);
const confirmCostInfo = ref({
costCrystal: 0,
currentBalance: 0,
balanceAfter: 0,
mintCount: 0,
nextTierHint: '',
nextTierCost: 0,
});
const lenticularLayers = ref([]);
@ -673,18 +636,16 @@ const selectAsset = async () => {
confirmCostInfo.value = {
costCrystal: costRes.data.cost_crystal || 0,
currentBalance: costRes.data.current_balance || 0,
balanceAfter: costRes.data.balance_after || 0,
mintCount: costRes.data.mint_count || 0,
nextTierHint: costRes.data.next_tier_hint || '',
nextTierCost: costRes.data.next_tier_cost || '',
};
} else {
confirmCostInfo.value = {
costCrystal: 100,
currentBalance: 0,
balanceAfter: 0,
mintCount: 0,
nextTierHint: '',
nextTierCost: 0,
};
}
} catch (e) {
@ -693,9 +654,8 @@ const selectAsset = async () => {
confirmCostInfo.value = {
costCrystal: 100,
currentBalance: 0,
balanceAfter: 0,
mintCount: 0,
nextTierHint: '',
nextTierCost: 0,
};
}
@ -775,7 +735,9 @@ onMounted(() => {
const imagesData = uni.getStorageSync(GENERATED_IMAGES_KEY);
if (!imagesData) {
uni.showToast({ title: '未找到生成的图片', icon: 'none' });
setTimeout(() => uni.navigateBack(), 1500);
setTimeout(() => uni.navigateTo({
url: '/pages/castlove/lenticular/lenticular-create'
}), 1500);
return;
}
const parsed = JSON.parse(imagesData);
@ -796,7 +758,9 @@ onMounted(() => {
} catch (e) {
console.error('[GenerationResult] 读取图片数据失败:', e);
uni.showToast({ title: '数据错误', icon: 'none' });
setTimeout(() => uni.navigateBack(), 1500);
setTimeout(() => uni.navigateTo({
url: '/pages/castlove/lenticular/lenticular-create'
}), 1500);
}
});
@ -852,10 +816,13 @@ onUnmounted(() => {
}
@keyframes starTwinkle {
0%, 100% {
0%,
100% {
opacity: 0.3;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.2);
@ -937,6 +904,7 @@ onUnmounted(() => {
transform: scale(0) translateY(100rpx);
opacity: 0;
}
100% {
transform: scale(1) translateY(0);
opacity: 1;
@ -968,9 +936,12 @@ onUnmounted(() => {
}
@keyframes cardFloat {
0%, 100% {
0%,
100% {
transform: translateY(0) rotate(var(--rotate, 0deg));
}
50% {
transform: translateY(-20rpx) rotate(var(--rotate, 0deg));
}
@ -1029,7 +1000,9 @@ onUnmounted(() => {
0% {
left: -100%;
}
50%, 100% {
50%,
100% {
left: 100%;
}
}
@ -1109,9 +1082,11 @@ onUnmounted(() => {
0% {
transform: translate(-50%, -50%) scale(0);
}
50% {
transform: translate(-50%, -50%) scale(1.2);
}
100% {
transform: translate(-50%, -50%) scale(1);
}
@ -1147,9 +1122,12 @@ onUnmounted(() => {
}
@keyframes giftFloat {
0%, 100% {
0%,
100% {
transform: translateX(-50%) translateY(0);
}
50% {
transform: translateX(-50%) translateY(-20rpx);
}
@ -1171,9 +1149,11 @@ onUnmounted(() => {
transform: scale(0.8);
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;

View File

@ -20,11 +20,8 @@
</view>
<!-- 镭射批量导出离屏 -->
<canvas
canvas-id="laserBatchCanvas"
class="laser-batch-canvas"
:style="{ width: laserCanvasW + 'px', height: laserCanvasH + 'px' }"
/>
<canvas canvas-id="laserBatchCanvas" class="laser-batch-canvas"
:style="{ width: laserCanvasW + 'px', height: laserCanvasH + 'px' }" />
</view>
</template>
@ -99,7 +96,9 @@ const revertProgress = () => {
clearInterval(revertInterval);
// 退
setTimeout(() => {
uni.navigateBack();
uni.navigateTo({
url: '/pages/castlove/lenticular/lenticular-create'
});
}, 500);
}
}, 100);
@ -206,7 +205,9 @@ onMounted(() => {
const requestDataStr = uni.getStorageSync(GENERATION_REQUEST_KEY);
if (!requestDataStr) {
uni.showToast({ title: '数据错误', icon: 'none' });
setTimeout(() => uni.navigateBack(), 1500);
setTimeout(() => uni.navigateTo({
url: '/pages/castlove/lenticular/lenticular-create'
}), 1500);
return;
}
generationData = JSON.parse(requestDataStr);
@ -225,7 +226,9 @@ onMounted(() => {
} catch (e) {
console.error('[GenerationLoading] mount', e);
uni.showToast({ title: '数据错误', icon: 'none' });
setTimeout(() => uni.navigateBack(), 1500);
setTimeout(() => uni.navigateTo({
url: '/pages/castlove/lenticular/lenticular-create'
}), 1500);
}
});
</script>
@ -268,10 +271,12 @@ onMounted(() => {
}
@keyframes float {
0%,
100% {
transform: translateX(-50%) translateY(0) rotate(0deg);
}
50% {
transform: translateX(-50%) translateY(-20rpx) rotate(2deg);
}
@ -336,10 +341,12 @@ onMounted(() => {
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}

View File

@ -445,6 +445,7 @@ const handleBack = () => {
.back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
.content-wrapper {

View File

@ -214,6 +214,7 @@ const handleUserInfoUpdate = () => {
const handleBalanceUpdate = (data) => {
if (userInfo.value) {
userInfo.value = { ...userInfo.value, crystal_balance: data.crystal_balance };
exhibitionRevenue.value = data.crystal_balance || 0;
} else {
loadUserInfo();
}

View File

@ -326,6 +326,7 @@ onUnmounted(() => {
.back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
.content-wrapper {

View File

@ -361,6 +361,7 @@ onUnmounted(() => {
.nav-back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
.nav-title {

View File

@ -1019,6 +1019,7 @@ onShow(() => {
.nav-back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
.nav-title {

View File

@ -55,16 +55,9 @@
</view>
<!-- 通用确认弹窗 -->
<ConfirmModal
:visible="confirmModal.visible"
:title="confirmModal.title"
:content="confirmModal.content"
:confirmText="confirmModal.confirmText"
:cancelText="confirmModal.cancelText"
:showCancel="confirmModal.showCancel"
@confirm="onConfirmModal"
@cancel="onCancelModal"
/>
<ConfirmModal :visible="confirmModal.visible" :title="confirmModal.title" :content="confirmModal.content"
:confirmText="confirmModal.confirmText" :cancelText="confirmModal.cancelText"
:showCancel="confirmModal.showCancel" @confirm="onConfirmModal" @cancel="onCancelModal" />
</template>
<script setup>
@ -406,8 +399,8 @@ const handleNext = async () => {
.back-icon {
font-size: 48rpx;
color: #000000;
font-weight: bold;
color: #fff;
}
.nav-title {

View File

@ -271,8 +271,8 @@ const handleNext = async () => {
.back-icon {
font-size: 48rpx;
color: #000000;
font-weight: bold;
color: #fff;
}
.nav-title {

View File

@ -10,7 +10,7 @@
<image :src="userInfo?.avatar_url || '/static/icon/avatar-default.png'" class="user-avatar" mode="aspectFill" />
<view class="user-balance">
<image src="/static/icon/crystal.png" class="balance-icon" mode="aspectFit" />
<text class="balance-text">{{ userInfo?.crystal_balance || 0 }}</text>
<text class="balance-text">{{ crystalBalance || 0 }}</text>
</view>
</view>
@ -77,6 +77,7 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { purchaseItem } from '@/utils/activity-config'
import { getEarningsSummaryApi } from '@/utils/api'
import DiamondConfirmModal from './DiamondConfirmModal.vue'
const props = defineProps({
@ -147,15 +148,31 @@ function handleModalCancel() {
}
const userInfo = ref(null);
const crystalBalance = ref(0)
function loadUserInfo() {
async function loadUserInfo() {
try {
const userStr = uni.getStorageSync('user')
if (userStr) {
userInfo.value = typeof userStr === 'string' ? JSON.parse(userStr) : userStr
}
// API
const res = await getEarningsSummaryApi()
if (res.code === 200 && res.data) {
crystalBalance.value = res.data.crystal_balance || 0
if (userInfo.value) {
userInfo.value.crystal_balance = crystalBalance.value
uni.setStorageSync('user', JSON.stringify(userInfo.value))
}
// Header
uni.$emit('balanceUpdated', { crystal_balance: crystalBalance.value })
} else {
crystalBalance.value = userInfo.value?.crystal_balance || 0
}
} catch (error) {
console.error('加载用户信息失败:', error)
crystalBalance.value = userInfo.value?.crystal_balance || 0
}
}
@ -183,6 +200,11 @@ onMounted(() => {
// 线
loadPendingActions()
//
if (items.value && items.value.length > 0) {
selectedItem.value = items.value[0].type
}
//
networkListener = uni.onNetworkStatusChange((res) => {
const wasOffline = !isOnline.value
@ -333,33 +355,22 @@ async function handleConfirmContribute() {
return
}
// API
let successCount = 0
let lastRemainingBalance = null
let lastErrorMessage = ''
for (let i = 0; i < quantity.value; i++) {
const res = await contributeItem(item, true, true)
console.log(`${i+1}次调用返回值:`, res)
if (res && res.success !== false) {
successCount++
lastRemainingBalance = res.remainingBalance
} else {
lastErrorMessage = res?.message || '活动不在进行中,无法购买'
}
}
// API
const res = await contributeItem(item, true, true, quantity.value)
console.log(`批量购买返回值:`, res)
// 使
if (lastRemainingBalance !== null) {
await updateLocalBalanceFromResult(lastRemainingBalance)
//
if (res && res.remainingBalance !== null) {
await updateLocalBalanceFromResult(res.remainingBalance)
}
//
if (successCount === quantity.value) {
showResultToast('✅', `贡献成功\n+${item.cost * successCount} 贡献值已到账`)
} else if (successCount > 0) {
showResultToast('⚠️', `部分成功 ${successCount}/${quantity.value}`)
if (res && res.success) {
showResultToast('✅', `贡献成功\n+${item.cost * quantity.value} 贡献值已到账`)
//
emit('contribute', item.type, res.remainingBalance)
} else {
showResultToast('❌', lastErrorMessage || '贡献失败')
showResultToast('❌', res?.message || '贡献失败')
}
} finally {
processingItems.delete(item.type)
@ -370,14 +381,11 @@ async function handleConfirmContribute() {
//
async function validateBalance(cost) {
try {
if (!userInfo.value) {
const userStr = uni.getStorageSync('user')
if (userStr) {
userInfo.value = typeof userStr === 'string' ? JSON.parse(userStr) : { ...userStr }
//
if (crystalBalance.value === 0 && userInfo.value?.crystal_balance) {
crystalBalance.value = userInfo.value.crystal_balance
}
}
const balance = Number(userInfo.value?.crystal_balance) || 0
return balance >= cost
return crystalBalance.value >= cost
} catch (error) {
console.error('验证余额失败:', error)
return false
@ -385,11 +393,11 @@ async function validateBalance(cost) {
}
//
async function contributeItem(item, isRetry = false, silent = false) {
async function contributeItem(item, isRetry = false, silent = false, qty = 1) {
try {
// 使 activity-config.js purchaseItem
const result = await purchaseItem(props.activityId, item.type, 1)
console.log(`[contributeItem] result:`, result, 'isRetry:', isRetry, 'silent:', silent)
const result = await purchaseItem(props.activityId, item.type, qty)
console.log(`[contributeItem] result:`, result, 'isRetry:', isRetry, 'silent:', silent, 'qty:', qty)
//
if (!result.success) {
@ -478,6 +486,7 @@ function deductLocalBalance(cost) {
//
uni.setStorageSync('user', JSON.stringify(user))
userInfo.value = user
crystalBalance.value = user.crystal_balance
uni.$emit('balanceUpdated', { crystal_balance: user.crystal_balance })
}
} catch (error) {
@ -504,6 +513,7 @@ function refundLocalBalance(cost) {
//
uni.setStorageSync('user', JSON.stringify(user))
userInfo.value = user
crystalBalance.value = user.crystal_balance
uni.$emit('balanceUpdated', { crystal_balance: user.crystal_balance })
}
} catch (error) {
@ -529,6 +539,7 @@ async function updateLocalBalanceFromResult(newBalance) {
//
uni.setStorageSync('user', JSON.stringify(user))
userInfo.value = user
crystalBalance.value = user.crystal_balance
uni.$emit('balanceUpdated', { crystal_balance: user.crystal_balance })
}
} catch (error) {

View File

@ -574,6 +574,7 @@ const handleCloseClick = (e) => {
.back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
/* Tab 样式 */

View File

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@ -681,6 +681,17 @@ export function purchaseActivityItemApi(activityId, itemType, quantity = 1) {
})
}
// 批量购买活动道具
export function batchPurchaseActivityItemsApi(activityId, items) {
return request({
url: `/api/v1/activities/${activityId}/batch-purchase`,
method: 'POST',
data: {
items: items
}
})
}
// 获取活动最新贡献记录(实时轮询用)
export function getActivityContributionsLatestApi(activityId, sinceTimestamp = 0, sinceId = 0, limit = 5) {
return request({