feat: 新增图片类型计算

This commit is contained in:
zerosaturation 2026-05-07 14:07:36 +08:00
parent 5d75d2067e
commit 1b82e4c3bc
12 changed files with 138 additions and 857 deletions

BIN
backend/assetService Executable file

Binary file not shown.

BIN
backend/galleryService Executable file

Binary file not shown.

View File

@ -171,6 +171,8 @@ func ConvertInspirationFlowData(pbData *pbGallery.InspirationFlowData) *GetInspi
CoverURL: item.CoverUrl,
LikeCount: item.LikeCount,
OwnerNickname: item.OwnerNickname,
Span: item.Span,
MaterialType: item.MaterialType,
})
}

View File

@ -96,10 +96,12 @@ type GetMyExhibitedAssetsResponseDTO struct {
// InspirationFlowItemDTO 灵感瀑布藏品项
type InspirationFlowItemDTO struct {
AssetID int64 `json:"asset_id"` // 资产ID
Name string `json:"name"` // 藏品名称
CoverURL string `json:"cover_url"` // 封面图URL
LikeCount int32 `json:"like_count"` // 点赞数
Name string `json:"name"` // 藏品名称
CoverURL string `json:"cover_url"` // 封面图URL
LikeCount int32 `json:"like_count"` // 点赞数
OwnerNickname string `json:"owner_nickname"` // 展出者昵称
Span int32 `json:"span"` // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
MaterialType string `json:"material_type"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
}
// GetInspirationFlowResponseDTO 获取灵感瀑布藏品列表响应

View File

@ -1,284 +0,0 @@
// Code generated by protoc-gen-triple. DO NOT EDIT.
//
// Source: gallery.proto
package gallery
import (
"context"
)
import (
"dubbo.apache.org/dubbo-go/v3"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
"dubbo.apache.org/dubbo-go/v3/server"
)
// This is a compile-time assertion to ensure that this generated file and the Triple package
// are compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of Triple newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of Triple or updating the Triple
// version compiled into your binary.
const _ = triple_protocol.IsAtLeastVersion0_1_0
const (
// GalleryServiceName is the fully-qualified name of the GalleryService service.
GalleryServiceName = "topfans.gallery.GalleryService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// GalleryServiceGetMyGalleryProcedure is the fully-qualified name of the GalleryService's GetMyGallery RPC.
GalleryServiceGetMyGalleryProcedure = "/topfans.gallery.GalleryService/GetMyGallery"
// GalleryServiceGetUserGalleryProcedure is the fully-qualified name of the GalleryService's GetUserGallery RPC.
GalleryServiceGetUserGalleryProcedure = "/topfans.gallery.GalleryService/GetUserGallery"
// GalleryServicePlaceAssetProcedure is the fully-qualified name of the GalleryService's PlaceAsset RPC.
GalleryServicePlaceAssetProcedure = "/topfans.gallery.GalleryService/PlaceAsset"
// GalleryServiceUnlockSlotProcedure is the fully-qualified name of the GalleryService's UnlockSlot RPC.
GalleryServiceUnlockSlotProcedure = "/topfans.gallery.GalleryService/UnlockSlot"
// GalleryServiceRemoveFromSlotProcedure is the fully-qualified name of the GalleryService's RemoveFromSlot RPC.
GalleryServiceRemoveFromSlotProcedure = "/topfans.gallery.GalleryService/RemoveFromSlot"
// GalleryServiceGetMyExhibitedAssetsProcedure is the fully-qualified name of the GalleryService's GetMyExhibitedAssets RPC.
GalleryServiceGetMyExhibitedAssetsProcedure = "/topfans.gallery.GalleryService/GetMyExhibitedAssets"
// GalleryServiceGetInspirationFlowProcedure is the fully-qualified name of the GalleryService's GetInspirationFlow RPC.
GalleryServiceGetInspirationFlowProcedure = "/topfans.gallery.GalleryService/GetInspirationFlow"
)
var (
_ GalleryService = (*GalleryServiceImpl)(nil)
)
// GalleryService is a client for the topfans.gallery.GalleryService service.
type GalleryService interface {
GetMyGallery(ctx context.Context, req *GetMyGalleryRequest, opts ...client.CallOption) (*GetMyGalleryResponse, error)
GetUserGallery(ctx context.Context, req *GetUserGalleryRequest, opts ...client.CallOption) (*GetUserGalleryResponse, error)
PlaceAsset(ctx context.Context, req *PlaceAssetRequest, opts ...client.CallOption) (*PlaceAssetResponse, error)
UnlockSlot(ctx context.Context, req *UnlockSlotRequest, opts ...client.CallOption) (*UnlockSlotResponse, error)
RemoveFromSlot(ctx context.Context, req *RemoveFromSlotRequest, opts ...client.CallOption) (*RemoveFromSlotResponse, error)
GetMyExhibitedAssets(ctx context.Context, req *GetMyExhibitedAssetsRequest, opts ...client.CallOption) (*GetMyExhibitedAssetsResponse, error)
GetInspirationFlow(ctx context.Context, req *GetInspirationFlowRequest, opts ...client.CallOption) (*GetInspirationFlowResponse, error)
}
// NewGalleryService constructs a client for the gallery.GalleryService service.
func NewGalleryService(cli *client.Client, opts ...client.ReferenceOption) (GalleryService, error) {
conn, err := cli.DialWithInfo("topfans.gallery.GalleryService", &GalleryService_ClientInfo, opts...)
if err != nil {
return nil, err
}
return &GalleryServiceImpl{
conn: conn,
}, nil
}
func SetConsumerGalleryService(srv common.RPCService) {
dubbo.SetConsumerServiceWithInfo(srv, &GalleryService_ClientInfo)
}
// GalleryServiceImpl implements GalleryService.
type GalleryServiceImpl struct {
conn *client.Connection
}
func (c *GalleryServiceImpl) GetMyGallery(ctx context.Context, req *GetMyGalleryRequest, opts ...client.CallOption) (*GetMyGalleryResponse, error) {
resp := new(GetMyGalleryResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetMyGallery", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *GalleryServiceImpl) GetUserGallery(ctx context.Context, req *GetUserGalleryRequest, opts ...client.CallOption) (*GetUserGalleryResponse, error) {
resp := new(GetUserGalleryResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetUserGallery", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *GalleryServiceImpl) PlaceAsset(ctx context.Context, req *PlaceAssetRequest, opts ...client.CallOption) (*PlaceAssetResponse, error) {
resp := new(PlaceAssetResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "PlaceAsset", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *GalleryServiceImpl) UnlockSlot(ctx context.Context, req *UnlockSlotRequest, opts ...client.CallOption) (*UnlockSlotResponse, error) {
resp := new(UnlockSlotResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "UnlockSlot", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *GalleryServiceImpl) RemoveFromSlot(ctx context.Context, req *RemoveFromSlotRequest, opts ...client.CallOption) (*RemoveFromSlotResponse, error) {
resp := new(RemoveFromSlotResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "RemoveFromSlot", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *GalleryServiceImpl) GetMyExhibitedAssets(ctx context.Context, req *GetMyExhibitedAssetsRequest, opts ...client.CallOption) (*GetMyExhibitedAssetsResponse, error) {
resp := new(GetMyExhibitedAssetsResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetMyExhibitedAssets", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *GalleryServiceImpl) GetInspirationFlow(ctx context.Context, req *GetInspirationFlowRequest, opts ...client.CallOption) (*GetInspirationFlowResponse, error) {
resp := new(GetInspirationFlowResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetInspirationFlow", opts...); err != nil {
return nil, err
}
return resp, nil
}
var GalleryService_ClientInfo = client.ClientInfo{
InterfaceName: "topfans.gallery.GalleryService",
MethodNames: []string{"GetMyGallery", "GetUserGallery", "PlaceAsset", "UnlockSlot", "RemoveFromSlot", "GetMyExhibitedAssets", "GetInspirationFlow"},
ConnectionInjectFunc: func(dubboCliRaw interface{}, conn *client.Connection) {
dubboCli := dubboCliRaw.(*GalleryServiceImpl)
dubboCli.conn = conn
},
}
// GalleryServiceHandler is an implementation of the topfans.gallery.GalleryService service.
type GalleryServiceHandler interface {
GetMyGallery(context.Context, *GetMyGalleryRequest) (*GetMyGalleryResponse, error)
GetUserGallery(context.Context, *GetUserGalleryRequest) (*GetUserGalleryResponse, error)
PlaceAsset(context.Context, *PlaceAssetRequest) (*PlaceAssetResponse, error)
UnlockSlot(context.Context, *UnlockSlotRequest) (*UnlockSlotResponse, error)
RemoveFromSlot(context.Context, *RemoveFromSlotRequest) (*RemoveFromSlotResponse, error)
GetMyExhibitedAssets(context.Context, *GetMyExhibitedAssetsRequest) (*GetMyExhibitedAssetsResponse, error)
GetInspirationFlow(context.Context, *GetInspirationFlowRequest) (*GetInspirationFlowResponse, error)
}
func RegisterGalleryServiceHandler(srv *server.Server, hdlr GalleryServiceHandler, opts ...server.ServiceOption) error {
return srv.Register(hdlr, &GalleryService_ServiceInfo, opts...)
}
func SetProviderGalleryService(srv common.RPCService) {
dubbo.SetProviderServiceWithInfo(srv, &GalleryService_ServiceInfo)
}
var GalleryService_ServiceInfo = server.ServiceInfo{
InterfaceName: "topfans.gallery.GalleryService",
ServiceType: (*GalleryServiceHandler)(nil),
Methods: []server.MethodInfo{
{
Name: "GetMyGallery",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetMyGalleryRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetMyGalleryRequest)
res, err := handler.(GalleryServiceHandler).GetMyGallery(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetUserGallery",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetUserGalleryRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetUserGalleryRequest)
res, err := handler.(GalleryServiceHandler).GetUserGallery(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "PlaceAsset",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(PlaceAssetRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*PlaceAssetRequest)
res, err := handler.(GalleryServiceHandler).PlaceAsset(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "UnlockSlot",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(UnlockSlotRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*UnlockSlotRequest)
res, err := handler.(GalleryServiceHandler).UnlockSlot(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "RemoveFromSlot",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(RemoveFromSlotRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*RemoveFromSlotRequest)
res, err := handler.(GalleryServiceHandler).RemoveFromSlot(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetMyExhibitedAssets",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetMyExhibitedAssetsRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetMyExhibitedAssetsRequest)
res, err := handler.(GalleryServiceHandler).GetMyExhibitedAssets(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetInspirationFlow",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetInspirationFlowRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetInspirationFlowRequest)
res, err := handler.(GalleryServiceHandler).GetInspirationFlow(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
},
}

View File

@ -1,554 +0,0 @@
// Code generated by protoc-gen-triple. DO NOT EDIT.
//
// Source: social.proto
package social
import (
"context"
)
import (
"dubbo.apache.org/dubbo-go/v3"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
"dubbo.apache.org/dubbo-go/v3/server"
)
// This is a compile-time assertion to ensure that this generated file and the Triple package
// are compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of Triple newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of Triple or updating the Triple
// version compiled into your binary.
const _ = triple_protocol.IsAtLeastVersion0_1_0
const (
// SocialServiceName is the fully-qualified name of the SocialService service.
SocialServiceName = "topfans.social.SocialService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// SocialServiceSendFriendRequestProcedure is the fully-qualified name of the SocialService's SendFriendRequest RPC.
SocialServiceSendFriendRequestProcedure = "/topfans.social.SocialService/SendFriendRequest"
// SocialServiceGetFriendRequestsProcedure is the fully-qualified name of the SocialService's GetFriendRequests RPC.
SocialServiceGetFriendRequestsProcedure = "/topfans.social.SocialService/GetFriendRequests"
// SocialServiceHandleFriendRequestProcedure is the fully-qualified name of the SocialService's HandleFriendRequest RPC.
SocialServiceHandleFriendRequestProcedure = "/topfans.social.SocialService/HandleFriendRequest"
// SocialServiceGetFriendListProcedure is the fully-qualified name of the SocialService's GetFriendList RPC.
SocialServiceGetFriendListProcedure = "/topfans.social.SocialService/GetFriendList"
// SocialServiceDeleteFriendProcedure is the fully-qualified name of the SocialService's DeleteFriend RPC.
SocialServiceDeleteFriendProcedure = "/topfans.social.SocialService/DeleteFriend"
// SocialServiceSetFriendRemarkProcedure is the fully-qualified name of the SocialService's SetFriendRemark RPC.
SocialServiceSetFriendRemarkProcedure = "/topfans.social.SocialService/SetFriendRemark"
// SocialServiceCheckFriendshipProcedure is the fully-qualified name of the SocialService's CheckFriendship RPC.
SocialServiceCheckFriendshipProcedure = "/topfans.social.SocialService/CheckFriendship"
// SocialServiceGetFriendCountProcedure is the fully-qualified name of the SocialService's GetFriendCount RPC.
SocialServiceGetFriendCountProcedure = "/topfans.social.SocialService/GetFriendCount"
// SocialServiceSearchUserForFriendProcedure is the fully-qualified name of the SocialService's SearchUserForFriend RPC.
SocialServiceSearchUserForFriendProcedure = "/topfans.social.SocialService/SearchUserForFriend"
// SocialServiceGetRandomUsersProcedure is the fully-qualified name of the SocialService's GetRandomUsers RPC.
SocialServiceGetRandomUsersProcedure = "/topfans.social.SocialService/GetRandomUsers"
// SocialServiceGetUsersPagedProcedure is the fully-qualified name of the SocialService's GetUsersPaged RPC.
SocialServiceGetUsersPagedProcedure = "/topfans.social.SocialService/GetUsersPaged"
// SocialServiceLikeAssetProcedure is the fully-qualified name of the SocialService's LikeAsset RPC.
SocialServiceLikeAssetProcedure = "/topfans.social.SocialService/LikeAsset"
// SocialServiceUnlikeAssetProcedure is the fully-qualified name of the SocialService's UnlikeAsset RPC.
SocialServiceUnlikeAssetProcedure = "/topfans.social.SocialService/UnlikeAsset"
// SocialServiceCheckAssetLikeProcedure is the fully-qualified name of the SocialService's CheckAssetLike RPC.
SocialServiceCheckAssetLikeProcedure = "/topfans.social.SocialService/CheckAssetLike"
// SocialServiceGetMyLikedAssetsProcedure is the fully-qualified name of the SocialService's GetMyLikedAssets RPC.
SocialServiceGetMyLikedAssetsProcedure = "/topfans.social.SocialService/GetMyLikedAssets"
// SocialServiceGetMyTodayLikedAssetsProcedure is the fully-qualified name of the SocialService's GetMyTodayLikedAssets RPC.
SocialServiceGetMyTodayLikedAssetsProcedure = "/topfans.social.SocialService/GetMyTodayLikedAssets"
// SocialServiceGetMyWeekLikedAssetsProcedure is the fully-qualified name of the SocialService's GetMyWeekLikedAssets RPC.
SocialServiceGetMyWeekLikedAssetsProcedure = "/topfans.social.SocialService/GetMyWeekLikedAssets"
)
var (
_ SocialService = (*SocialServiceImpl)(nil)
)
// SocialService is a client for the topfans.social.SocialService service.
type SocialService interface {
SendFriendRequest(ctx context.Context, req *SendFriendRequestRequest, opts ...client.CallOption) (*SendFriendRequestResponse, error)
GetFriendRequests(ctx context.Context, req *GetFriendRequestsRequest, opts ...client.CallOption) (*GetFriendRequestsResponse, error)
HandleFriendRequest(ctx context.Context, req *HandleFriendRequestRequest, opts ...client.CallOption) (*HandleFriendRequestResponse, error)
GetFriendList(ctx context.Context, req *GetFriendListRequest, opts ...client.CallOption) (*GetFriendListResponse, error)
DeleteFriend(ctx context.Context, req *DeleteFriendRequest, opts ...client.CallOption) (*DeleteFriendResponse, error)
SetFriendRemark(ctx context.Context, req *SetFriendRemarkRequest, opts ...client.CallOption) (*SetFriendRemarkResponse, error)
CheckFriendship(ctx context.Context, req *CheckFriendshipRequest, opts ...client.CallOption) (*CheckFriendshipResponse, error)
GetFriendCount(ctx context.Context, req *GetFriendCountRequest, opts ...client.CallOption) (*GetFriendCountResponse, error)
SearchUserForFriend(ctx context.Context, req *SearchUserForFriendRequest, opts ...client.CallOption) (*SearchUserForFriendResponse, error)
GetRandomUsers(ctx context.Context, req *GetRandomUsersRequest, opts ...client.CallOption) (*GetRandomUsersResponse, error)
GetUsersPaged(ctx context.Context, req *GetUsersPagedRequest, opts ...client.CallOption) (*GetUsersPagedResponse, error)
LikeAsset(ctx context.Context, req *LikeAssetRequest, opts ...client.CallOption) (*LikeAssetResponse, error)
UnlikeAsset(ctx context.Context, req *UnlikeAssetRequest, opts ...client.CallOption) (*UnlikeAssetResponse, error)
CheckAssetLike(ctx context.Context, req *CheckAssetLikeRequest, opts ...client.CallOption) (*CheckAssetLikeResponse, error)
GetMyLikedAssets(ctx context.Context, req *GetMyLikedAssetsRequest, opts ...client.CallOption) (*GetMyLikedAssetsResponse, error)
GetMyTodayLikedAssets(ctx context.Context, req *GetMyTodayLikedAssetsRequest, opts ...client.CallOption) (*GetMyTodayLikedAssetsResponse, error)
GetMyWeekLikedAssets(ctx context.Context, req *GetMyWeekLikedAssetsRequest, opts ...client.CallOption) (*GetMyWeekLikedAssetsResponse, error)
}
// NewSocialService constructs a client for the social.SocialService service.
func NewSocialService(cli *client.Client, opts ...client.ReferenceOption) (SocialService, error) {
conn, err := cli.DialWithInfo("topfans.social.SocialService", &SocialService_ClientInfo, opts...)
if err != nil {
return nil, err
}
return &SocialServiceImpl{
conn: conn,
}, nil
}
func SetConsumerSocialService(srv common.RPCService) {
dubbo.SetConsumerServiceWithInfo(srv, &SocialService_ClientInfo)
}
// SocialServiceImpl implements SocialService.
type SocialServiceImpl struct {
conn *client.Connection
}
func (c *SocialServiceImpl) SendFriendRequest(ctx context.Context, req *SendFriendRequestRequest, opts ...client.CallOption) (*SendFriendRequestResponse, error) {
resp := new(SendFriendRequestResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "SendFriendRequest", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetFriendRequests(ctx context.Context, req *GetFriendRequestsRequest, opts ...client.CallOption) (*GetFriendRequestsResponse, error) {
resp := new(GetFriendRequestsResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetFriendRequests", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) HandleFriendRequest(ctx context.Context, req *HandleFriendRequestRequest, opts ...client.CallOption) (*HandleFriendRequestResponse, error) {
resp := new(HandleFriendRequestResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "HandleFriendRequest", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetFriendList(ctx context.Context, req *GetFriendListRequest, opts ...client.CallOption) (*GetFriendListResponse, error) {
resp := new(GetFriendListResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetFriendList", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) DeleteFriend(ctx context.Context, req *DeleteFriendRequest, opts ...client.CallOption) (*DeleteFriendResponse, error) {
resp := new(DeleteFriendResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "DeleteFriend", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) SetFriendRemark(ctx context.Context, req *SetFriendRemarkRequest, opts ...client.CallOption) (*SetFriendRemarkResponse, error) {
resp := new(SetFriendRemarkResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "SetFriendRemark", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) CheckFriendship(ctx context.Context, req *CheckFriendshipRequest, opts ...client.CallOption) (*CheckFriendshipResponse, error) {
resp := new(CheckFriendshipResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "CheckFriendship", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetFriendCount(ctx context.Context, req *GetFriendCountRequest, opts ...client.CallOption) (*GetFriendCountResponse, error) {
resp := new(GetFriendCountResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetFriendCount", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) SearchUserForFriend(ctx context.Context, req *SearchUserForFriendRequest, opts ...client.CallOption) (*SearchUserForFriendResponse, error) {
resp := new(SearchUserForFriendResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "SearchUserForFriend", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetRandomUsers(ctx context.Context, req *GetRandomUsersRequest, opts ...client.CallOption) (*GetRandomUsersResponse, error) {
resp := new(GetRandomUsersResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetRandomUsers", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetUsersPaged(ctx context.Context, req *GetUsersPagedRequest, opts ...client.CallOption) (*GetUsersPagedResponse, error) {
resp := new(GetUsersPagedResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetUsersPaged", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) LikeAsset(ctx context.Context, req *LikeAssetRequest, opts ...client.CallOption) (*LikeAssetResponse, error) {
resp := new(LikeAssetResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "LikeAsset", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) UnlikeAsset(ctx context.Context, req *UnlikeAssetRequest, opts ...client.CallOption) (*UnlikeAssetResponse, error) {
resp := new(UnlikeAssetResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "UnlikeAsset", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) CheckAssetLike(ctx context.Context, req *CheckAssetLikeRequest, opts ...client.CallOption) (*CheckAssetLikeResponse, error) {
resp := new(CheckAssetLikeResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "CheckAssetLike", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetMyLikedAssets(ctx context.Context, req *GetMyLikedAssetsRequest, opts ...client.CallOption) (*GetMyLikedAssetsResponse, error) {
resp := new(GetMyLikedAssetsResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetMyLikedAssets", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetMyTodayLikedAssets(ctx context.Context, req *GetMyTodayLikedAssetsRequest, opts ...client.CallOption) (*GetMyTodayLikedAssetsResponse, error) {
resp := new(GetMyTodayLikedAssetsResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetMyTodayLikedAssets", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *SocialServiceImpl) GetMyWeekLikedAssets(ctx context.Context, req *GetMyWeekLikedAssetsRequest, opts ...client.CallOption) (*GetMyWeekLikedAssetsResponse, error) {
resp := new(GetMyWeekLikedAssetsResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetMyWeekLikedAssets", opts...); err != nil {
return nil, err
}
return resp, nil
}
var SocialService_ClientInfo = client.ClientInfo{
InterfaceName: "topfans.social.SocialService",
MethodNames: []string{"SendFriendRequest", "GetFriendRequests", "HandleFriendRequest", "GetFriendList", "DeleteFriend", "SetFriendRemark", "CheckFriendship", "GetFriendCount", "SearchUserForFriend", "GetRandomUsers", "GetUsersPaged", "LikeAsset", "UnlikeAsset", "CheckAssetLike", "GetMyLikedAssets", "GetMyTodayLikedAssets", "GetMyWeekLikedAssets"},
ConnectionInjectFunc: func(dubboCliRaw interface{}, conn *client.Connection) {
dubboCli := dubboCliRaw.(*SocialServiceImpl)
dubboCli.conn = conn
},
}
// SocialServiceHandler is an implementation of the topfans.social.SocialService service.
type SocialServiceHandler interface {
SendFriendRequest(context.Context, *SendFriendRequestRequest) (*SendFriendRequestResponse, error)
GetFriendRequests(context.Context, *GetFriendRequestsRequest) (*GetFriendRequestsResponse, error)
HandleFriendRequest(context.Context, *HandleFriendRequestRequest) (*HandleFriendRequestResponse, error)
GetFriendList(context.Context, *GetFriendListRequest) (*GetFriendListResponse, error)
DeleteFriend(context.Context, *DeleteFriendRequest) (*DeleteFriendResponse, error)
SetFriendRemark(context.Context, *SetFriendRemarkRequest) (*SetFriendRemarkResponse, error)
CheckFriendship(context.Context, *CheckFriendshipRequest) (*CheckFriendshipResponse, error)
GetFriendCount(context.Context, *GetFriendCountRequest) (*GetFriendCountResponse, error)
SearchUserForFriend(context.Context, *SearchUserForFriendRequest) (*SearchUserForFriendResponse, error)
GetRandomUsers(context.Context, *GetRandomUsersRequest) (*GetRandomUsersResponse, error)
GetUsersPaged(context.Context, *GetUsersPagedRequest) (*GetUsersPagedResponse, error)
LikeAsset(context.Context, *LikeAssetRequest) (*LikeAssetResponse, error)
UnlikeAsset(context.Context, *UnlikeAssetRequest) (*UnlikeAssetResponse, error)
CheckAssetLike(context.Context, *CheckAssetLikeRequest) (*CheckAssetLikeResponse, error)
GetMyLikedAssets(context.Context, *GetMyLikedAssetsRequest) (*GetMyLikedAssetsResponse, error)
GetMyTodayLikedAssets(context.Context, *GetMyTodayLikedAssetsRequest) (*GetMyTodayLikedAssetsResponse, error)
GetMyWeekLikedAssets(context.Context, *GetMyWeekLikedAssetsRequest) (*GetMyWeekLikedAssetsResponse, error)
}
func RegisterSocialServiceHandler(srv *server.Server, hdlr SocialServiceHandler, opts ...server.ServiceOption) error {
return srv.Register(hdlr, &SocialService_ServiceInfo, opts...)
}
func SetProviderSocialService(srv common.RPCService) {
dubbo.SetProviderServiceWithInfo(srv, &SocialService_ServiceInfo)
}
var SocialService_ServiceInfo = server.ServiceInfo{
InterfaceName: "topfans.social.SocialService",
ServiceType: (*SocialServiceHandler)(nil),
Methods: []server.MethodInfo{
{
Name: "SendFriendRequest",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(SendFriendRequestRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*SendFriendRequestRequest)
res, err := handler.(SocialServiceHandler).SendFriendRequest(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetFriendRequests",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetFriendRequestsRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetFriendRequestsRequest)
res, err := handler.(SocialServiceHandler).GetFriendRequests(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "HandleFriendRequest",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(HandleFriendRequestRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*HandleFriendRequestRequest)
res, err := handler.(SocialServiceHandler).HandleFriendRequest(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetFriendList",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetFriendListRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetFriendListRequest)
res, err := handler.(SocialServiceHandler).GetFriendList(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "DeleteFriend",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(DeleteFriendRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*DeleteFriendRequest)
res, err := handler.(SocialServiceHandler).DeleteFriend(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "SetFriendRemark",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(SetFriendRemarkRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*SetFriendRemarkRequest)
res, err := handler.(SocialServiceHandler).SetFriendRemark(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "CheckFriendship",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(CheckFriendshipRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*CheckFriendshipRequest)
res, err := handler.(SocialServiceHandler).CheckFriendship(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetFriendCount",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetFriendCountRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetFriendCountRequest)
res, err := handler.(SocialServiceHandler).GetFriendCount(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "SearchUserForFriend",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(SearchUserForFriendRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*SearchUserForFriendRequest)
res, err := handler.(SocialServiceHandler).SearchUserForFriend(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetRandomUsers",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetRandomUsersRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetRandomUsersRequest)
res, err := handler.(SocialServiceHandler).GetRandomUsers(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetUsersPaged",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetUsersPagedRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetUsersPagedRequest)
res, err := handler.(SocialServiceHandler).GetUsersPaged(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "LikeAsset",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(LikeAssetRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*LikeAssetRequest)
res, err := handler.(SocialServiceHandler).LikeAsset(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "UnlikeAsset",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(UnlikeAssetRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*UnlikeAssetRequest)
res, err := handler.(SocialServiceHandler).UnlikeAsset(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "CheckAssetLike",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(CheckAssetLikeRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*CheckAssetLikeRequest)
res, err := handler.(SocialServiceHandler).CheckAssetLike(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetMyLikedAssets",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetMyLikedAssetsRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetMyLikedAssetsRequest)
res, err := handler.(SocialServiceHandler).GetMyLikedAssets(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetMyTodayLikedAssets",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetMyTodayLikedAssetsRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetMyTodayLikedAssetsRequest)
res, err := handler.(SocialServiceHandler).GetMyTodayLikedAssets(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetMyWeekLikedAssets",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetMyWeekLikedAssetsRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetMyWeekLikedAssetsRequest)
res, err := handler.(SocialServiceHandler).GetMyWeekLikedAssets(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
},
}

View File

@ -1422,6 +1422,8 @@ type InspirationFlowItem struct {
CoverUrl string `protobuf:"bytes,3,opt,name=cover_url,json=coverUrl,proto3" json:"cover_url,omitempty"` // 封面图URL
LikeCount int32 `protobuf:"varint,4,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 点赞数
OwnerNickname string `protobuf:"bytes,5,opt,name=owner_nickname,json=ownerNickname,proto3" json:"owner_nickname,omitempty"` // 展出者昵称
Span int32 `protobuf:"varint,6,opt,name=span,proto3" json:"span,omitempty"` // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
MaterialType string `protobuf:"bytes,7,opt,name=material_type,json=materialType,proto3" json:"material_type,omitempty"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -1491,6 +1493,20 @@ func (x *InspirationFlowItem) GetOwnerNickname() string {
return ""
}
func (x *InspirationFlowItem) GetSpan() int32 {
if x != nil {
return x.Span
}
return 0
}
func (x *InspirationFlowItem) GetMaterialType() string {
if x != nil {
return x.MaterialType
}
return ""
}
var File_gallery_proto protoreflect.FileDescriptor
const file_gallery_proto_rawDesc = "" +
@ -1602,14 +1618,16 @@ const file_gallery_proto_rawDesc = "" +
"\x06cursor\x18\x02 \x01(\tR\x06cursor\x12\x19\n" +
"\bhas_more\x18\x03 \x01(\bR\ahasMore\x12\x1d\n" +
"\n" +
"session_id\x18\x04 \x01(\tR\tsessionId\"\xa7\x01\n" +
"session_id\x18\x04 \x01(\tR\tsessionId\"\xe0\x01\n" +
"\x13InspirationFlowItem\x12\x19\n" +
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" +
"\tcover_url\x18\x03 \x01(\tR\bcoverUrl\x12\x1d\n" +
"\n" +
"like_count\x18\x04 \x01(\x05R\tlikeCount\x12%\n" +
"\x0eowner_nickname\x18\x05 \x01(\tR\rownerNickname2\xc6\a\n" +
"\x0eowner_nickname\x18\x05 \x01(\tR\rownerNickname\x12\x12\n" +
"\x04span\x18\x06 \x01(\x05R\x04span\x12#\n" +
"\rmaterial_type\x18\a \x01(\tR\fmaterialType2\xc6\a\n" +
"\x0eGalleryService\x12u\n" +
"\fGetMyGallery\x12$.topfans.gallery.GetMyGalleryRequest\x1a%.topfans.gallery.GetMyGalleryResponse\"\x18\x82\xd3\xe4\x93\x02\x12\x12\x10/api/mygalleries\x12\x86\x01\n" +
"\x0eGetUserGallery\x12&.topfans.gallery.GetUserGalleryRequest\x1a'.topfans.gallery.GetUserGalleryResponse\"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/api/galleries/{target_uid}\x12v\n" +

View File

@ -219,6 +219,8 @@ message InspirationFlowItem {
int64 asset_id = 1; // ID
string name = 2; //
string cover_url = 3; // URL
int32 like_count = 4; //
int32 like_count = 4; //
string owner_nickname = 5; //
int32 span = 6; // : 0-301, 31-1002, 101-2003, 200+4
string material_type = 7; // : hot(), potential(), new()
}

View File

@ -0,0 +1,4 @@
-- migrate_add_material_type_to_assets.sql
-- 为 assets 表添加 material_type 字段
ALTER TABLE assets ADD COLUMN IF NOT EXISTS material_type VARCHAR(50);

View File

@ -0,0 +1,9 @@
-- 修复 asset_likes 表唯一约束,添加 star_id
-- 原约束: (user_id, asset_id)
-- 新约束: (user_id, asset_id, star_id)
-- 删除旧约束
ALTER TABLE asset_likes DROP CONSTRAINT IF EXISTS uk_asset_likes_user_asset;
-- 添加新约束(包含 star_id
ALTER TABLE asset_likes ADD CONSTRAINT uk_asset_likes_user_asset_star UNIQUE (user_id, asset_id, star_id);

View File

@ -45,6 +45,9 @@ type AssetRepository interface {
// DecrementLikeCount 减少点赞数
DecrementLikeCount(assetID int64) error
// UpdateMaterialTypeByLikes 根据点赞数和创建时间更新素材类型
UpdateMaterialTypeByLikes(assetID int64, likes int32, createdAt int64) error
// IsExhibiting 检查资产是否正在展出中
IsExhibiting(assetID int64) (bool, error)
}
@ -295,7 +298,26 @@ func (r *assetRepository) DecrementLikeCount(assetID int64) error {
UpdateColumn("like_count", gorm.Expr("like_count - ?", 1)).Error
}
// IsExhibiting 检查资产是否正在展出中
// UpdateMaterialTypeByLikes 根据点赞数和创建时间更新素材类型
func (r *assetRepository) UpdateMaterialTypeByLikes(assetID int64, likes int32, createdAt int64) error {
if assetID <= 0 {
return errors.New("asset_id must be greater than 0")
}
now := time.Now().UnixMilli()
var materialType string
if likes > 20 {
materialType = "hot"
} else if createdAt > 0 && now-createdAt < 3600*1000 && likes >= 10 {
materialType = "potential"
} else {
materialType = "new"
}
return r.db.Model(&models.Asset{}).
Where("id = ?", assetID).
UpdateColumn("material_type", materialType).Error
}
// 通过关联 exhibitions 表,查询未过期的展出记录
func (r *assetRepository) IsExhibiting(assetID int64) (bool, error) {
if assetID <= 0 {

View File

@ -65,6 +65,9 @@ type InspirationFlowItem struct {
CoverURL string
LikeCount int32
OwnerNickname string
Span int32 // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
MaterialType string // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
CreatedAt int64 // 创建时间(用于判断是否为潜力之星)
}
// ExhibitedAssetInfo 我展出的作品信息
@ -308,9 +311,9 @@ func (r *galleryRepository) CountValidExhibitions(starID int64, materialType str
now := time.Now().UnixMilli()
query := r.db.Model(&models.Exhibition{}).
Where("occupier_star_id = ? AND expire_at > ? AND deleted_at IS NULL", starID, now)
Where("exhibitions.occupier_star_id = ? AND exhibitions.expire_at > ? AND exhibitions.deleted_at IS NULL", starID, now)
if materialType != "" && materialType != "all" {
if materialType != "" && materialType != "all" && materialType != "random" {
query = query.Joins("JOIN assets a ON a.id = exhibitions.asset_id").
Where("a.material_type = ?", materialType)
}
@ -328,7 +331,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri
baseQuery := r.db.Model(&models.Exhibition{}).
Where("exhibitions.occupier_star_id = ? AND exhibitions.expire_at > ? AND exhibitions.deleted_at IS NULL", starID, now)
if materialType != "" && materialType != "all" {
if materialType != "" && materialType != "all" && materialType != "random" {
baseQuery = baseQuery.Joins("JOIN assets a ON a.id = exhibitions.asset_id").
Where("a.material_type = ?", materialType)
}
@ -339,23 +342,80 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri
}
// 执行随机排序查询
err := baseQuery.
Select(`exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname`).
Joins("JOIN assets a ON a.id = exhibitions.asset_id").
Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id").
Where("a.status = 1 AND a.is_active = true").
Order("RANDOM()").
Limit(limit).
Offset(offset).
Scan(&items).Error
var err error
if materialType == "" || materialType == "all" || materialType == "random" {
err = baseQuery.
Select(`exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname, a.material_type, a.created_at`).
Joins("JOIN assets a ON a.id = exhibitions.asset_id").
Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id").
Where("a.status = 1 AND a.is_active = true").
Order("RANDOM()").
Limit(limit).
Offset(offset).
Scan(&items).Error
} else {
// baseQuery 已经包含了 assets JOIN不需要重复添加
err = baseQuery.
Select(`exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname, a.material_type, a.created_at`).
Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id").
Where("a.status = 1 AND a.is_active = true").
Order("RANDOM()").
Limit(limit).
Offset(offset).
Scan(&items).Error
}
if err != nil {
return nil, err
}
// Read-repair: 检查并修正 material_type查询时自动修正数据不一致
for _, item := range items {
item.Span = calcSpanByLikes(item.LikeCount)
correctType := calcMaterialType(item.LikeCount, item.CreatedAt)
if item.MaterialType != correctType {
// 异步修正,不阻塞返回(使用 goroutine 避免影响响应时间)
go func(assetID int64, correct string) {
r.db.Model(&models.Asset{}).
Where("id = ?", assetID).
Update("material_type", correct)
}(item.AssetID, correctType)
// 同时修正当前对象的值,保证返回给前端的是正确的
item.MaterialType = correctType
}
}
return items, nil
}
// calcMaterialType 根据点赞数计算素材类型
func calcMaterialType(likes int32, createdAt int64) string {
now := time.Now().UnixMilli()
isWithin1Hour := createdAt > 0 && now-createdAt < 3600*1000
if likes > 20 {
return "hot"
}
// 上架1小时内且点赞>=10为潜力之星
if isWithin1Hour && likes >= 10 {
return "potential"
}
return "new"
}
// calcSpanByLikes 根据点赞数计算卡片大小
// 0-30 → span 1, 31-100 → span 2, 101-200 → span 3, 200+ → span 4
func calcSpanByLikes(likes int32) int32 {
if likes <= 30 {
return 1
} else if likes <= 100 {
return 2
} else if likes <= 200 {
return 3
}
return 4
}
// ==================== 辅助函数 ====================
// generateHostProfileID 生成 host_profile_id