chore(backend): remove starbookService + proto (config 4.2 cleanup)
- 删 services/starbookService 目录 (Phase 2 决策, owner 已迁 assetService); - 删 backend/proto/starbook.proto (proto 源, 不再被任何 service 引用)。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7ff2d5cdc4
commit
f46464fdf9
@ -1,100 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package topfans.starbook;
|
||||
|
||||
option go_package = "github.com/topfans/backend/pkg/proto/starbook;starbook";
|
||||
|
||||
import "proto/common.proto";
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
// 星册服务 - 典藏/活动藏品体系重构
|
||||
service StarbookService {
|
||||
// 星册首页
|
||||
rpc GetStarbookHome(GetStarbookHomeRequest) returns (GetStarbookHomeResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/api/v1/starbook/home"
|
||||
};
|
||||
}
|
||||
|
||||
// 藏品列表(分页)
|
||||
rpc GetStarbookItems(GetStarbookItemsRequest) returns (GetStarbookItemsResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/api/v1/starbook/items"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 星册首页 ====================
|
||||
|
||||
// 星册首页请求
|
||||
message GetStarbookHomeRequest {
|
||||
}
|
||||
|
||||
// 星册首页响应
|
||||
message GetStarbookHomeResponse {
|
||||
topfans.common.BaseResponse base = 1;
|
||||
StarbookHomeData data = 2;
|
||||
}
|
||||
|
||||
// 星册首页数据
|
||||
message StarbookHomeData {
|
||||
repeated AssetGroup groups = 1;
|
||||
}
|
||||
|
||||
// 资产分组
|
||||
message AssetGroup {
|
||||
string type = 1; // 'regular' / 'collection' / 'activity'
|
||||
string category = 2; // 'castlove'(regular) / collection_category / activity_type
|
||||
string category_name = 3;
|
||||
// regular 使用 grades 分组;collection/activity 使用 flat items 列表
|
||||
repeated GradeSection grades = 4; // 仅 regular 时有效
|
||||
repeated AssetItem items = 5; // collection / activity 时有效
|
||||
int32 total_count = 6;
|
||||
bool has_more = 7;
|
||||
}
|
||||
|
||||
// 等级分组(仅 regular 类型使用)
|
||||
message GradeSection {
|
||||
int32 grade = 1; // 等级:1/2/3/4/5...
|
||||
repeated AssetItem items = 2;
|
||||
int32 total_count = 3;
|
||||
bool has_more = 4;
|
||||
}
|
||||
|
||||
// 资产项
|
||||
message AssetItem {
|
||||
int64 asset_id = 1;
|
||||
string name = 2;
|
||||
string cover_url_signed = 3; // 预签名封面URL
|
||||
int32 like_count = 4;
|
||||
int64 created_at = 5;
|
||||
string category = 6; // regular: 'castlove' / collection: category / activity: activity_type
|
||||
int32 grade = 7; // 仅 regular 时有效(1/2/3...),其他类型为 0
|
||||
int32 display_status = 8; // 展示状态:0=待展示, 1=已展示
|
||||
}
|
||||
|
||||
// ==================== 藏品列表(分页) ====================
|
||||
|
||||
// 藏品列表请求
|
||||
message GetStarbookItemsRequest {
|
||||
string type = 1; // 'regular' / 'collection' / 'activity'
|
||||
string category = 2; // regular 时固定传 'castlove'
|
||||
int32 grade = 3; // 仅 regular 时有效(1/2/3...)
|
||||
int32 page = 4; // 默认 1
|
||||
int32 page_size = 5; // 默认 20
|
||||
}
|
||||
|
||||
// 藏品列表响应
|
||||
message GetStarbookItemsResponse {
|
||||
topfans.common.BaseResponse base = 1;
|
||||
AssetListData data = 2;
|
||||
}
|
||||
|
||||
// 藏品列表数据
|
||||
message AssetListData {
|
||||
repeated AssetItem items = 1;
|
||||
int64 total = 2;
|
||||
int32 page = 3;
|
||||
int32 page_size = 4;
|
||||
bool has_more = 5;
|
||||
}
|
||||
@ -1,203 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
_ "dubbo.apache.org/dubbo-go/v3/imports"
|
||||
"dubbo.apache.org/dubbo-go/v3/protocol"
|
||||
"dubbo.apache.org/dubbo-go/v3/server"
|
||||
|
||||
"github.com/topfans/backend/pkg/database"
|
||||
"github.com/topfans/backend/pkg/health"
|
||||
"github.com/topfans/backend/pkg/logger"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
pb "github.com/topfans/backend/pkg/proto/starbook"
|
||||
assetRepo "github.com/topfans/backend/services/assetService/repository"
|
||||
"github.com/topfans/backend/services/starbookService/provider"
|
||||
"github.com/topfans/backend/services/starbookService/repository"
|
||||
"github.com/topfans/backend/services/starbookService/service"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.Int("port", getEnvInt("PORT", 20005), "Dubbo service port")
|
||||
dbHost = flag.String("db-host", getEnv("DB_HOST", "localhost"), "Database host")
|
||||
dbPort = flag.Int("db-port", getEnvInt("DB_PORT", 5432), "Database port")
|
||||
dbUser = flag.String("db-user", getEnv("DB_USER", "postgres"), "Database user")
|
||||
dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password")
|
||||
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
|
||||
healthHandler *health.Handler
|
||||
)
|
||||
|
||||
func getEnv(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func getEnvInt(key string, fallback int) int {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// 初始化日志(必须在最前面)
|
||||
env := os.Getenv("ENV")
|
||||
if env == "" {
|
||||
env = "development"
|
||||
}
|
||||
|
||||
if err := logger.Init(logger.Config{
|
||||
ServiceName: "starbook-service",
|
||||
Environment: env,
|
||||
LogLevel: os.Getenv("LOG_LEVEL"),
|
||||
}); err != nil {
|
||||
panic(fmt.Sprintf("Failed to initialize logger: %v", err))
|
||||
}
|
||||
defer logger.Sync()
|
||||
|
||||
logger.Sugar.Info("Starting Starbook Service...")
|
||||
|
||||
// 初始化数据库
|
||||
if err := initDatabase(); err != nil {
|
||||
logger.Sugar.Fatalf("Failed to initialize database: %v", err)
|
||||
}
|
||||
|
||||
// 自动迁移数据库表
|
||||
if err := autoMigrate(); err != nil {
|
||||
logger.Sugar.Fatalf("Failed to migrate database: %v", err)
|
||||
}
|
||||
|
||||
// 初始化 Repository
|
||||
registryRepo := repository.NewAssetRegistryRepository(database.GetDB())
|
||||
collectionRepo := repository.NewCollectionRepository(database.GetDB())
|
||||
activityRepo := repository.NewActivityAssetRepository(database.GetDB())
|
||||
assetRepository := assetRepo.NewAssetRepository(database.GetDB())
|
||||
|
||||
// 初始化 Service
|
||||
starbookService := service.NewStarbookService(
|
||||
database.GetDB(),
|
||||
registryRepo,
|
||||
assetRepository,
|
||||
collectionRepo,
|
||||
activityRepo,
|
||||
)
|
||||
|
||||
// 初始化 Provider
|
||||
starbookProvider := provider.NewStarbookProvider(starbookService)
|
||||
|
||||
// 初始化 Dubbo-go 服务器
|
||||
if err := initDubboService(starbookProvider); err != nil {
|
||||
logger.Sugar.Fatalf("Failed to initialize Dubbo service: %v", err)
|
||||
}
|
||||
|
||||
// 等待信号(优雅关闭)
|
||||
logger.Sugar.Info("Starbook service started successfully. Press Ctrl+C to exit.")
|
||||
gracefulShutdown()
|
||||
}
|
||||
|
||||
// initDatabase 初始化数据库连接
|
||||
func initDatabase() error {
|
||||
config := database.Config{
|
||||
Host: *dbHost,
|
||||
Port: *dbPort,
|
||||
User: *dbUser,
|
||||
Password: *dbPassword,
|
||||
DBName: *dbName,
|
||||
SSLMode: "disable",
|
||||
TimeZone: "Asia/Shanghai",
|
||||
}
|
||||
|
||||
return database.Init(config)
|
||||
}
|
||||
|
||||
// autoMigrate 自动迁移数据库表
|
||||
func autoMigrate() error {
|
||||
db := database.GetDB()
|
||||
if db == nil {
|
||||
return fmt.Errorf("database is not initialized")
|
||||
}
|
||||
|
||||
// 迁移星册相关的表
|
||||
tables := []interface{}{
|
||||
&models.CollectionAsset{},
|
||||
&models.ActivityAsset{},
|
||||
&models.AssetRegistry{},
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
if err := db.AutoMigrate(table); err != nil {
|
||||
return fmt.Errorf("failed to migrate table: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Sugar.Info("Database migration completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// initDubboService 初始化 Dubbo 服务
|
||||
func initDubboService(starbookProvider *provider.StarbookProvider) error {
|
||||
// 启动健康检查 HTTP 服务器
|
||||
healthPort := *port + 1000 // e.g., 20005 -> 21005
|
||||
healthHandler = health.NewHandler("starbook-service", healthPort)
|
||||
healthHandler.Start()
|
||||
|
||||
// 创建 Dubbo Server
|
||||
srv, err := server.NewServer(
|
||||
server.WithServerProtocol(
|
||||
protocol.WithPort(*port),
|
||||
protocol.WithTriple(),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Dubbo server: %w", err)
|
||||
}
|
||||
|
||||
// 注册服务
|
||||
if err := pb.RegisterStarbookServiceHandler(srv, starbookProvider); err != nil {
|
||||
return fmt.Errorf("failed to register StarbookService handler: %w", err)
|
||||
}
|
||||
|
||||
logger.Sugar.Infof("Dubbo-go provider registered successfully, service: topfans.starbook.StarbookService, port: %d", *port)
|
||||
|
||||
// 在后台启动 Dubbo 服务器
|
||||
go func() {
|
||||
if err := srv.Serve(); err != nil {
|
||||
logger.Sugar.Fatalf("Failed to serve Dubbo: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// gracefulShutdown 优雅关闭
|
||||
func gracefulShutdown() {
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
logger.Sugar.Info("Shutting down Starbook Service...")
|
||||
|
||||
// 关闭健康检查服务器
|
||||
if healthHandler != nil {
|
||||
healthHandler.Stop()
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
if err := database.Close(); err != nil {
|
||||
logger.Sugar.Errorf("Error closing database: %v", err)
|
||||
}
|
||||
|
||||
logger.Sugar.Info("Starbook Service stopped")
|
||||
}
|
||||
@ -1,220 +0,0 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"dubbo.apache.org/dubbo-go/v3/common/constant"
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/logger"
|
||||
pb "github.com/topfans/backend/pkg/proto/starbook"
|
||||
pbCommon "github.com/topfans/backend/pkg/proto/common"
|
||||
"github.com/topfans/backend/services/starbookService/service"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
// StarbookProvider 星册服务Provider实现
|
||||
// 实现 Triple 协议生成的 StarbookServiceHandler 接口
|
||||
type StarbookProvider struct {
|
||||
starbookService service.StarbookService
|
||||
}
|
||||
|
||||
// 确保 StarbookProvider 实现了 StarbookServiceHandler 接口
|
||||
var _ pb.StarbookServiceHandler = (*StarbookProvider)(nil)
|
||||
|
||||
// NewStarbookProvider 创建星册服务Provider实例
|
||||
func NewStarbookProvider(starbookService service.StarbookService) *StarbookProvider {
|
||||
return &StarbookProvider{
|
||||
starbookService: starbookService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetStarbookHome 获取星册首页
|
||||
func (p *StarbookProvider) GetStarbookHome(ctx context.Context, req *pb.GetStarbookHomeRequest) (*pb.GetStarbookHomeResponse, error) {
|
||||
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
|
||||
if err != nil {
|
||||
return &pb.GetStarbookHomeResponse{
|
||||
Base: &pbCommon.BaseResponse{
|
||||
Code: uint32(codes.Unauthenticated),
|
||||
Message: "user authentication required",
|
||||
Timestamp: 0,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
resp, err := p.starbookService.GetStarbookHome(userID, starID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("GetStarbookHome failed",
|
||||
zap.Error(err),
|
||||
)
|
||||
return &pb.GetStarbookHomeResponse{
|
||||
Base: &pbCommon.BaseResponse{
|
||||
Code: uint32(toStatusCode(err)),
|
||||
Message: err.Error(),
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetStarbookItems 获取星册藏品列表
|
||||
func (p *StarbookProvider) GetStarbookItems(ctx context.Context, req *pb.GetStarbookItemsRequest) (*pb.GetStarbookItemsResponse, error) {
|
||||
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
|
||||
if err != nil {
|
||||
return &pb.GetStarbookItemsResponse{
|
||||
Base: &pbCommon.BaseResponse{
|
||||
Code: uint32(codes.Unauthenticated),
|
||||
Message: "user authentication required",
|
||||
Timestamp: 0,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
resp, err := p.starbookService.GetStarbookItems(req, userID, starID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("GetStarbookItems failed",
|
||||
zap.Error(err),
|
||||
)
|
||||
return &pb.GetStarbookItemsResponse{
|
||||
Base: &pbCommon.BaseResponse{
|
||||
Code: uint32(toStatusCode(err)),
|
||||
Message: err.Error(),
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// extractUserInfoFromDubboAttachments 从 Dubbo attachments 中提取用户信息
|
||||
// 网关调用:网关已验证 Token 并将 user_id 和 star_id 通过 attachments 传递
|
||||
func extractUserInfoFromDubboAttachments(ctx context.Context) (int64, int64, error) {
|
||||
logger.Logger.Info("=== extractUserInfoFromDubboAttachments called ===")
|
||||
|
||||
// 使用 constant.AttachmentKey 获取 Dubbo attachments
|
||||
attachments := ctx.Value(constant.AttachmentKey)
|
||||
logger.Logger.Info("ctx.Value(constant.AttachmentKey) result",
|
||||
zap.Any("attachments", attachments),
|
||||
zap.String("type", fmt.Sprintf("%T", attachments)),
|
||||
)
|
||||
|
||||
if attachments != nil {
|
||||
logger.Logger.Info("Attachments found in context")
|
||||
|
||||
if attMap, ok := attachments.(map[string]interface{}); ok {
|
||||
logger.Logger.Info("Attachments is map[string]interface{}",
|
||||
zap.Int("map_size", len(attMap)))
|
||||
userID, starID := extractUserInfoFromMap(attMap)
|
||||
if userID > 0 && starID > 0 {
|
||||
logger.Logger.Info("Successfully extracted user info",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("star_id", starID),
|
||||
)
|
||||
return userID, starID, nil
|
||||
}
|
||||
logger.Logger.Warn("Extracted zero user_id or star_id",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("star_id", starID),
|
||||
)
|
||||
} else {
|
||||
logger.Logger.Warn("Attachments is not map[string]interface{}",
|
||||
zap.String("actual_type", fmt.Sprintf("%T", attachments)),
|
||||
zap.Any("value", attachments),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
logger.Logger.Warn("No attachments found in context")
|
||||
// 尝试打印所有 context 的值
|
||||
logger.Logger.Warn("Context type", zap.String("type", fmt.Sprintf("%T", ctx)))
|
||||
}
|
||||
|
||||
return 0, 0, fmt.Errorf("user info not found in Dubbo attachments (expected user_id and star_id from gateway)")
|
||||
}
|
||||
|
||||
// getContextKeys 获取 context 中所有的 key
|
||||
func getContextKeys(ctx context.Context) []string {
|
||||
keys := make([]string, 0)
|
||||
// 直接打印 ctx 类型
|
||||
logger.Logger.Debug("Context type", zap.String("type", fmt.Sprintf("%T", ctx)))
|
||||
return keys
|
||||
}
|
||||
|
||||
// getContextValues 遍历 context 中的一些已知值
|
||||
func getContextValues(ctx context.Context) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
// 检查 constant.AttachmentKey
|
||||
if v := ctx.Value(constant.AttachmentKey); v != nil {
|
||||
result["AttachmentKey"] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// extractUserInfoFromMap 从 map 中提取 user_id 和 star_id
|
||||
// 支持多种类型:int64, float64, string
|
||||
func extractUserInfoFromMap(attMap map[string]interface{}) (int64, int64) {
|
||||
var userID, starID int64
|
||||
|
||||
// 打印所有 key 和 value
|
||||
for k, v := range attMap {
|
||||
logger.Logger.Debug("Attachment map entry",
|
||||
zap.String("key", k),
|
||||
zap.Any("value", v),
|
||||
zap.String("type", fmt.Sprintf("%T", v)),
|
||||
)
|
||||
}
|
||||
|
||||
// 提取 user_id
|
||||
if v, ok := attMap["user_id"]; ok {
|
||||
userID = parseIntValue(v)
|
||||
}
|
||||
|
||||
// 提取 star_id
|
||||
if v, ok := attMap["star_id"]; ok {
|
||||
starID = parseIntValue(v)
|
||||
}
|
||||
|
||||
return userID, starID
|
||||
}
|
||||
|
||||
// parseIntValue 解析不同类型的整数值
|
||||
func parseIntValue(v interface{}) int64 {
|
||||
switch val := v.(type) {
|
||||
case int64:
|
||||
return val
|
||||
case int:
|
||||
return int64(val)
|
||||
case float64:
|
||||
return int64(val)
|
||||
case string:
|
||||
if i, err := strconv.ParseInt(val, 10, 64); err == nil {
|
||||
return i
|
||||
}
|
||||
case []string:
|
||||
if len(val) > 0 {
|
||||
if i, err := strconv.ParseInt(val[0], 10, 64); err == nil {
|
||||
return i
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
if len(val) > 0 {
|
||||
if s, ok := val[0].(string); ok {
|
||||
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// toStatusCode 将错误转换为 google.rpc.Code
|
||||
// 包装 appErrors.ToGRPCCode 以便该 provider 内调用更简洁
|
||||
func toStatusCode(err error) codes.Code {
|
||||
return appErrors.ToGRPCCode(err)
|
||||
}
|
||||
@ -1,247 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ActivityAssetRepository 活动藏品Repository接口
|
||||
type ActivityAssetRepository interface {
|
||||
// Create 创建活动藏品
|
||||
Create(asset *models.ActivityAsset) error
|
||||
|
||||
// GetByID 根据ID查询
|
||||
GetByID(id int64) (*models.ActivityAsset, error)
|
||||
|
||||
// GetByAssetID 根据asset_id查询
|
||||
GetByAssetID(assetID int64) (*models.ActivityAsset, error)
|
||||
|
||||
// GetByAssetIDs 批量查询
|
||||
GetByAssetIDs(assetIDs []int64) ([]*models.ActivityAsset, error)
|
||||
|
||||
// GetByOwner 查询用户的活动藏品列表
|
||||
GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.ActivityAsset, error)
|
||||
|
||||
// GetByOwnerAndActivityType 查询用户指定活动类型的活动藏品
|
||||
GetByOwnerAndActivityType(ownerUID, starID int64, activityType string, limit, offset int) ([]*models.ActivityAsset, error)
|
||||
|
||||
// GetByOwnerAndActivityID 查询用户指定活动的活动藏品
|
||||
GetByOwnerAndActivityID(ownerUID, starID int64, activityID int64, limit, offset int) ([]*models.ActivityAsset, error)
|
||||
|
||||
// CountByOwner 统计用户的活动藏品数量
|
||||
CountByOwner(ownerUID, starID int64) (int64, error)
|
||||
|
||||
// CountByOwnerAndActivityType 统计用户指定活动类型的活动藏品数量
|
||||
CountByOwnerAndActivityType(ownerUID, starID int64, activityType string) (int64, error)
|
||||
|
||||
// UpdateLikeCount 更新点赞数
|
||||
UpdateLikeCount(id int64, likeCount int32) error
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
IncrementLikeCount(id int64) error
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
DecrementLikeCount(id int64) error
|
||||
}
|
||||
|
||||
// activityAssetRepository 活动藏品Repository实现
|
||||
type activityAssetRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewActivityAssetRepository 创建活动藏品Repository实例
|
||||
func NewActivityAssetRepository(db *gorm.DB) ActivityAssetRepository {
|
||||
return &activityAssetRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建活动藏品
|
||||
func (r *activityAssetRepository) Create(asset *models.ActivityAsset) error {
|
||||
if asset == nil {
|
||||
return errors.New("activity asset cannot be nil")
|
||||
}
|
||||
if asset.OwnerUID <= 0 {
|
||||
return errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if asset.StarID <= 0 {
|
||||
return errors.New("star_id must be greater than 0")
|
||||
}
|
||||
return r.db.Create(asset).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID查询
|
||||
func (r *activityAssetRepository) GetByID(id int64) (*models.ActivityAsset, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New("id must be greater than 0")
|
||||
}
|
||||
var asset models.ActivityAsset
|
||||
if err := r.db.Where("id = ?", id).First(&asset).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrActivityAssetNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &asset, nil
|
||||
}
|
||||
|
||||
// GetByAssetID 根据asset_id查询
|
||||
func (r *activityAssetRepository) GetByAssetID(assetID int64) (*models.ActivityAsset, error) {
|
||||
if assetID <= 0 {
|
||||
return nil, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
var asset models.ActivityAsset
|
||||
if err := r.db.Where("asset_id = ?", assetID).First(&asset).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrActivityAssetNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &asset, nil
|
||||
}
|
||||
|
||||
// GetByAssetIDs 批量查询
|
||||
func (r *activityAssetRepository) GetByAssetIDs(assetIDs []int64) ([]*models.ActivityAsset, error) {
|
||||
if len(assetIDs) == 0 {
|
||||
return []*models.ActivityAsset{}, nil
|
||||
}
|
||||
var assets []*models.ActivityAsset
|
||||
if err := r.db.Where("asset_id IN ?", assetIDs).Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// GetByOwner 查询用户的活动藏品列表
|
||||
func (r *activityAssetRepository) GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.ActivityAsset, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var assets []*models.ActivityAsset
|
||||
query := r.db.Where("owner_uid = ? AND star_id = ?", ownerUID, starID).
|
||||
Order("created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndActivityType 查询用户指定活动类型的活动藏品
|
||||
func (r *activityAssetRepository) GetByOwnerAndActivityType(ownerUID, starID int64, activityType string, limit, offset int) ([]*models.ActivityAsset, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var assets []*models.ActivityAsset
|
||||
query := r.db.Where("owner_uid = ? AND star_id = ? AND activity_type = ?", ownerUID, starID, activityType).
|
||||
Order("created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndActivityID 查询用户指定活动的活动藏品
|
||||
func (r *activityAssetRepository) GetByOwnerAndActivityID(ownerUID, starID int64, activityID int64, limit, offset int) ([]*models.ActivityAsset, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var assets []*models.ActivityAsset
|
||||
query := r.db.Where("owner_uid = ? AND star_id = ? AND activity_id = ?", ownerUID, starID, activityID).
|
||||
Order("created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// CountByOwner 统计用户的活动藏品数量
|
||||
func (r *activityAssetRepository) CountByOwner(ownerUID, starID int64) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.ActivityAsset{}).
|
||||
Where("owner_uid = ? AND star_id = ?", ownerUID, starID).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByOwnerAndActivityType 统计用户指定活动类型的活动藏品数量
|
||||
func (r *activityAssetRepository) CountByOwnerAndActivityType(ownerUID, starID int64, activityType string) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.ActivityAsset{}).
|
||||
Where("owner_uid = ? AND star_id = ? AND activity_type = ?", ownerUID, starID, activityType).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// UpdateLikeCount 更新点赞数
|
||||
func (r *activityAssetRepository) UpdateLikeCount(id int64, likeCount int32) error {
|
||||
if id <= 0 {
|
||||
return errors.New("id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.ActivityAsset{}).
|
||||
Where("id = ?", id).
|
||||
Update("like_count", likeCount).Error
|
||||
}
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
func (r *activityAssetRepository) IncrementLikeCount(id int64) error {
|
||||
if id <= 0 {
|
||||
return errors.New("id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.ActivityAsset{}).
|
||||
Where("id = ?", id).
|
||||
UpdateColumn("like_count", gorm.Expr("like_count + ?", 1)).Error
|
||||
}
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
func (r *activityAssetRepository) DecrementLikeCount(id int64) error {
|
||||
if id <= 0 {
|
||||
return errors.New("id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.ActivityAsset{}).
|
||||
Where("id = ? AND like_count > ?", id, 0).
|
||||
UpdateColumn("like_count", gorm.Expr("like_count - ?", 1)).Error
|
||||
}
|
||||
@ -1,387 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AssetRegistryRepository 资产统一索引Repository接口
|
||||
type AssetRegistryRepository interface {
|
||||
// Create 创建索引记录
|
||||
Create(registry *models.AssetRegistry) error
|
||||
|
||||
// GetByID 根据ID查询
|
||||
GetByID(id int64) (*models.AssetRegistry, error)
|
||||
|
||||
// GetByAssetID 根据asset_id查询
|
||||
GetByAssetID(assetID int64) (*models.AssetRegistry, error)
|
||||
|
||||
// GetByAssetTypeAndID 根据类型和asset_id查询
|
||||
GetByAssetTypeAndID(assetType string, assetID int64) (*models.AssetRegistry, error)
|
||||
|
||||
// GetByOwner 查询用户的所有索引记录
|
||||
GetByOwner(ownerUID, starID int64) ([]*models.AssetRegistry, error)
|
||||
|
||||
// GetByOwnerAndType 查询用户指定类型的索引记录
|
||||
GetByOwnerAndType(ownerUID, starID int64, assetType string, limit, offset int) ([]*models.AssetRegistry, error)
|
||||
|
||||
// GetByOwnerAndTypeAndGrade 查询用户指定类型和等级的索引记录
|
||||
GetByOwnerAndTypeAndGrade(ownerUID, starID int64, assetType string, grade int32, limit, offset int) ([]*models.AssetRegistry, error)
|
||||
|
||||
// GetByOwnerAndTypeAndCategory 查询用户指定类型和分类的索引记录
|
||||
GetByOwnerAndTypeAndCategory(ownerUID, starID int64, assetType string, category string, limit, offset int) ([]*models.AssetRegistry, error)
|
||||
|
||||
// GetByOwnerAndTypeAndActivity 查询用户指定类型和活动的索引记录
|
||||
GetByOwnerAndTypeAndActivity(ownerUID, starID int64, assetType string, activityID int64, limit, offset int) ([]*models.AssetRegistry, error)
|
||||
|
||||
// CountByOwner 统计用户的索引记录数量
|
||||
CountByOwner(ownerUID, starID int64) (int64, error)
|
||||
|
||||
// CountByOwnerAndType 统计用户指定类型的索引记录数量
|
||||
CountByOwnerAndType(ownerUID, starID int64, assetType string) (int64, error)
|
||||
|
||||
// CountByOwnerAndTypeAndGrade 统计用户指定类型和等级的索引记录数量
|
||||
CountByOwnerAndTypeAndGrade(ownerUID, starID int64, assetType string, grade int32) (int64, error)
|
||||
|
||||
// CountByOwnerAndTypeAndCategory 统计用户指定类型和分类的索引记录数量
|
||||
CountByOwnerAndTypeAndCategory(ownerUID, starID int64, assetType string, category string) (int64, error)
|
||||
|
||||
// CountByOwnerAndTypeAndActivity 统计用户指定类型和活动的索引记录数量
|
||||
CountByOwnerAndTypeAndActivity(ownerUID, starID int64, assetType string, activityID int64) (int64, error)
|
||||
|
||||
// UpdateLikeCount 更新点赞数
|
||||
UpdateLikeCount(assetID int64, likeCount int32) error
|
||||
|
||||
// UpdateGrade 更新等级
|
||||
UpdateGrade(assetID int64, grade int32) error
|
||||
|
||||
// Delete 删除索引记录
|
||||
Delete(assetID int64) error
|
||||
|
||||
// DeleteByAssetType 删除指定类型的索引记录
|
||||
DeleteByAssetType(assetType string, assetID int64) error
|
||||
}
|
||||
|
||||
// assetRegistryRepository 资产统一索引Repository实现
|
||||
type assetRegistryRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewAssetRegistryRepository 创建资产统一索引Repository实例
|
||||
func NewAssetRegistryRepository(db *gorm.DB) AssetRegistryRepository {
|
||||
return &assetRegistryRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建索引记录
|
||||
func (r *assetRegistryRepository) Create(registry *models.AssetRegistry) error {
|
||||
if registry == nil {
|
||||
return errors.New("registry cannot be nil")
|
||||
}
|
||||
if registry.OwnerUID <= 0 {
|
||||
return errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if registry.StarID <= 0 {
|
||||
return errors.New("star_id must be greater than 0")
|
||||
}
|
||||
return r.db.Create(registry).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID查询
|
||||
func (r *assetRegistryRepository) GetByID(id int64) (*models.AssetRegistry, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New("id must be greater than 0")
|
||||
}
|
||||
var registry models.AssetRegistry
|
||||
if err := r.db.Where("id = ?", id).First(®istry).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrAssetRegistryNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ®istry, nil
|
||||
}
|
||||
|
||||
// GetByAssetID 根据asset_id查询
|
||||
func (r *assetRegistryRepository) GetByAssetID(assetID int64) (*models.AssetRegistry, error) {
|
||||
if assetID <= 0 {
|
||||
return nil, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
var registry models.AssetRegistry
|
||||
if err := r.db.Where("asset_id = ?", assetID).First(®istry).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrAssetRegistryNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ®istry, nil
|
||||
}
|
||||
|
||||
// GetByAssetTypeAndID 根据类型和asset_id查询
|
||||
func (r *assetRegistryRepository) GetByAssetTypeAndID(assetType string, assetID int64) (*models.AssetRegistry, error) {
|
||||
if assetType == "" {
|
||||
return nil, errors.New("asset_type must not be empty")
|
||||
}
|
||||
if assetID <= 0 {
|
||||
return nil, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
var registry models.AssetRegistry
|
||||
if err := r.db.Where("asset_type = ? AND asset_id = ?", assetType, assetID).First(®istry).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrAssetRegistryNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ®istry, nil
|
||||
}
|
||||
|
||||
// GetByOwner 查询用户的所有索引记录
|
||||
func (r *assetRegistryRepository) GetByOwner(ownerUID, starID int64) ([]*models.AssetRegistry, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var registries []*models.AssetRegistry
|
||||
if err := r.db.
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ?", ownerUID, starID).
|
||||
Order("asset_registry.created_at DESC").
|
||||
Find(®istries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndType 查询用户指定类型的索引记录
|
||||
func (r *assetRegistryRepository) GetByOwnerAndType(ownerUID, starID int64, assetType string, limit, offset int) ([]*models.AssetRegistry, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var registries []*models.AssetRegistry
|
||||
query := r.db.
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ?", ownerUID, starID, assetType).
|
||||
Order("asset_registry.created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(®istries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndTypeAndGrade 查询用户指定类型和等级的索引记录
|
||||
func (r *assetRegistryRepository) GetByOwnerAndTypeAndGrade(ownerUID, starID int64, assetType string, grade int32, limit, offset int) ([]*models.AssetRegistry, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var registries []*models.AssetRegistry
|
||||
query := r.db.
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ? AND asset_registry.grade = ?", ownerUID, starID, assetType, grade).
|
||||
Order("asset_registry.created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(®istries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndTypeAndCategory 查询用户指定类型和分类的索引记录
|
||||
func (r *assetRegistryRepository) GetByOwnerAndTypeAndCategory(ownerUID, starID int64, assetType string, category string, limit, offset int) ([]*models.AssetRegistry, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var registries []*models.AssetRegistry
|
||||
query := r.db.
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ? AND asset_registry.collection_category = ?", ownerUID, starID, assetType, category).
|
||||
Order("asset_registry.created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(®istries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndTypeAndActivity 查询用户指定类型和活动的索引记录
|
||||
func (r *assetRegistryRepository) GetByOwnerAndTypeAndActivity(ownerUID, starID int64, assetType string, activityID int64, limit, offset int) ([]*models.AssetRegistry, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var registries []*models.AssetRegistry
|
||||
query := r.db.
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ? AND asset_registry.activity_id = ?", ownerUID, starID, assetType, activityID).
|
||||
Order("asset_registry.created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(®istries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// CountByOwner 统计用户的索引记录数量
|
||||
func (r *assetRegistryRepository) CountByOwner(ownerUID, starID int64) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.AssetRegistry{}).
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ?", ownerUID, starID).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByOwnerAndType 统计用户指定类型的索引记录数量
|
||||
func (r *assetRegistryRepository) CountByOwnerAndType(ownerUID, starID int64, assetType string) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.AssetRegistry{}).
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ?", ownerUID, starID, assetType).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByOwnerAndTypeAndGrade 统计用户指定类型和等级的索引记录数量
|
||||
func (r *assetRegistryRepository) CountByOwnerAndTypeAndGrade(ownerUID, starID int64, assetType string, grade int32) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.AssetRegistry{}).
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ? AND asset_registry.grade = ?", ownerUID, starID, assetType, grade).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByOwnerAndTypeAndCategory 统计用户指定类型和分类的索引记录数量
|
||||
func (r *assetRegistryRepository) CountByOwnerAndTypeAndCategory(ownerUID, starID int64, assetType string, category string) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.AssetRegistry{}).
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ? AND asset_registry.collection_category = ?", ownerUID, starID, assetType, category).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByOwnerAndTypeAndActivity 统计用户指定类型和活动的索引记录数量
|
||||
func (r *assetRegistryRepository) CountByOwnerAndTypeAndActivity(ownerUID, starID int64, assetType string, activityID int64) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.AssetRegistry{}).
|
||||
Joins("JOIN assets ON assets.id = asset_registry.asset_id AND assets.deleted_at IS NULL").
|
||||
Where("asset_registry.owner_uid = ? AND asset_registry.star_id = ? AND asset_registry.asset_type = ? AND asset_registry.activity_id = ?", ownerUID, starID, assetType, activityID).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// UpdateLikeCount 更新点赞数
|
||||
func (r *assetRegistryRepository) UpdateLikeCount(assetID int64, likeCount int32) error {
|
||||
if assetID <= 0 {
|
||||
return errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.AssetRegistry{}).
|
||||
Where("asset_id = ?", assetID).
|
||||
Update("like_count", likeCount).Error
|
||||
}
|
||||
|
||||
// UpdateGrade 更新等级
|
||||
func (r *assetRegistryRepository) UpdateGrade(assetID int64, grade int32) error {
|
||||
if assetID <= 0 {
|
||||
return errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.AssetRegistry{}).
|
||||
Where("asset_id = ?", assetID).
|
||||
Update("grade", grade).Error
|
||||
}
|
||||
|
||||
// Delete 删除索引记录
|
||||
func (r *assetRegistryRepository) Delete(assetID int64) error {
|
||||
if assetID <= 0 {
|
||||
return errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
return r.db.Where("asset_id = ?", assetID).Delete(&models.AssetRegistry{}).Error
|
||||
}
|
||||
|
||||
// DeleteByAssetType 删除指定类型的索引记录
|
||||
func (r *assetRegistryRepository) DeleteByAssetType(assetType string, assetID int64) error {
|
||||
if assetType == "" {
|
||||
return errors.New("asset_type must not be empty")
|
||||
}
|
||||
if assetID <= 0 {
|
||||
return errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
return r.db.Where("asset_type = ? AND asset_id = ?", assetType, assetID).
|
||||
Delete(&models.AssetRegistry{}).Error
|
||||
}
|
||||
@ -1,221 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CollectionRepository 典藏藏品Repository接口
|
||||
type CollectionRepository interface {
|
||||
// Create 创建典藏藏品
|
||||
Create(asset *models.CollectionAsset) error
|
||||
|
||||
// GetByID 根据ID查询
|
||||
GetByID(id int64) (*models.CollectionAsset, error)
|
||||
|
||||
// GetByAssetID 根据asset_id查询
|
||||
GetByAssetID(assetID int64) (*models.CollectionAsset, error)
|
||||
|
||||
// GetByAssetIDs 批量查询
|
||||
GetByAssetIDs(assetIDs []int64) ([]*models.CollectionAsset, error)
|
||||
|
||||
// GetByOwner 查询用户的典藏藏品列表
|
||||
GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.CollectionAsset, error)
|
||||
|
||||
// GetByOwnerAndCategory 查询用户指定分类的典藏藏品
|
||||
GetByOwnerAndCategory(ownerUID, starID int64, category string, limit, offset int) ([]*models.CollectionAsset, error)
|
||||
|
||||
// CountByOwner 统计用户的典藏藏品数量
|
||||
CountByOwner(ownerUID, starID int64) (int64, error)
|
||||
|
||||
// CountByOwnerAndCategory 统计用户指定分类的典藏藏品数量
|
||||
CountByOwnerAndCategory(ownerUID, starID int64, category string) (int64, error)
|
||||
|
||||
// UpdateLikeCount 更新点赞数
|
||||
UpdateLikeCount(id int64, likeCount int32) error
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
IncrementLikeCount(id int64) error
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
DecrementLikeCount(id int64) error
|
||||
}
|
||||
|
||||
// collectionRepository 典藏藏品Repository实现
|
||||
type collectionRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCollectionRepository 创建典藏藏品Repository实例
|
||||
func NewCollectionRepository(db *gorm.DB) CollectionRepository {
|
||||
return &collectionRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建典藏藏品
|
||||
func (r *collectionRepository) Create(asset *models.CollectionAsset) error {
|
||||
if asset == nil {
|
||||
return errors.New("collection asset cannot be nil")
|
||||
}
|
||||
if asset.OwnerUID <= 0 {
|
||||
return errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if asset.StarID <= 0 {
|
||||
return errors.New("star_id must be greater than 0")
|
||||
}
|
||||
return r.db.Create(asset).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID查询
|
||||
func (r *collectionRepository) GetByID(id int64) (*models.CollectionAsset, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New("id must be greater than 0")
|
||||
}
|
||||
var asset models.CollectionAsset
|
||||
if err := r.db.Where("id = ?", id).First(&asset).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrCollectionAssetNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &asset, nil
|
||||
}
|
||||
|
||||
// GetByAssetID 根据asset_id查询
|
||||
func (r *collectionRepository) GetByAssetID(assetID int64) (*models.CollectionAsset, error) {
|
||||
if assetID <= 0 {
|
||||
return nil, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
var asset models.CollectionAsset
|
||||
if err := r.db.Where("asset_id = ?", assetID).First(&asset).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, appErrors.ErrCollectionAssetNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &asset, nil
|
||||
}
|
||||
|
||||
// GetByAssetIDs 批量查询
|
||||
func (r *collectionRepository) GetByAssetIDs(assetIDs []int64) ([]*models.CollectionAsset, error) {
|
||||
if len(assetIDs) == 0 {
|
||||
return []*models.CollectionAsset{}, nil
|
||||
}
|
||||
var assets []*models.CollectionAsset
|
||||
if err := r.db.Where("asset_id IN ?", assetIDs).Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// GetByOwner 查询用户的典藏藏品列表
|
||||
func (r *collectionRepository) GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.CollectionAsset, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var assets []*models.CollectionAsset
|
||||
query := r.db.Where("owner_uid = ? AND star_id = ?", ownerUID, starID).
|
||||
Order("created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// GetByOwnerAndCategory 查询用户指定分类的典藏藏品
|
||||
func (r *collectionRepository) GetByOwnerAndCategory(ownerUID, starID int64, category string, limit, offset int) ([]*models.CollectionAsset, error) {
|
||||
if ownerUID <= 0 {
|
||||
return nil, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return nil, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var assets []*models.CollectionAsset
|
||||
query := r.db.Where("owner_uid = ? AND star_id = ? AND category = ?", ownerUID, starID, category).
|
||||
Order("created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
if err := query.Find(&assets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
// CountByOwner 统计用户的典藏藏品数量
|
||||
func (r *collectionRepository) CountByOwner(ownerUID, starID int64) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.CollectionAsset{}).
|
||||
Where("owner_uid = ? AND star_id = ?", ownerUID, starID).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByOwnerAndCategory 统计用户指定分类的典藏藏品数量
|
||||
func (r *collectionRepository) CountByOwnerAndCategory(ownerUID, starID int64, category string) (int64, error) {
|
||||
if ownerUID <= 0 {
|
||||
return 0, errors.New("owner_uid must be greater than 0")
|
||||
}
|
||||
if starID <= 0 {
|
||||
return 0, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
var count int64
|
||||
if err := r.db.Model(&models.CollectionAsset{}).
|
||||
Where("owner_uid = ? AND star_id = ? AND category = ?", ownerUID, starID, category).
|
||||
Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// UpdateLikeCount 更新点赞数
|
||||
func (r *collectionRepository) UpdateLikeCount(id int64, likeCount int32) error {
|
||||
if id <= 0 {
|
||||
return errors.New("id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.CollectionAsset{}).
|
||||
Where("id = ?", id).
|
||||
Update("like_count", likeCount).Error
|
||||
}
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
func (r *collectionRepository) IncrementLikeCount(id int64) error {
|
||||
if id <= 0 {
|
||||
return errors.New("id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.CollectionAsset{}).
|
||||
Where("id = ?", id).
|
||||
UpdateColumn("like_count", gorm.Expr("like_count + ?", 1)).Error
|
||||
}
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
func (r *collectionRepository) DecrementLikeCount(id int64) error {
|
||||
if id <= 0 {
|
||||
return errors.New("id must be greater than 0")
|
||||
}
|
||||
return r.db.Model(&models.CollectionAsset{}).
|
||||
Where("id = ? AND like_count > ?", id, 0).
|
||||
UpdateColumn("like_count", gorm.Expr("like_count - ?", 1)).Error
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
"github.com/topfans/backend/services/starbookService/repository"
|
||||
)
|
||||
|
||||
// ActivityAssetService 活动藏品服务接口
|
||||
type ActivityAssetService interface {
|
||||
// Create 创建活动藏品
|
||||
Create(asset *models.ActivityAsset) error
|
||||
|
||||
// GetByID 根据ID获取
|
||||
GetByID(id int64) (*models.ActivityAsset, error)
|
||||
|
||||
// GetByAssetID 根据asset_id获取
|
||||
GetByAssetID(assetID int64) (*models.ActivityAsset, error)
|
||||
|
||||
// GetByOwner 获取用户的活动藏品
|
||||
GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.ActivityAsset, error)
|
||||
|
||||
// GetByOwnerAndActivityType 获取用户指定活动类型的活动藏品
|
||||
GetByOwnerAndActivityType(ownerUID, starID int64, activityType string, limit, offset int) ([]*models.ActivityAsset, error)
|
||||
|
||||
// GetByOwnerAndActivityID 获取用户指定活动的活动藏品
|
||||
GetByOwnerAndActivityID(ownerUID, starID int64, activityID int64, limit, offset int) ([]*models.ActivityAsset, error)
|
||||
|
||||
// CountByOwner 统计用户的活动藏品数量
|
||||
CountByOwner(ownerUID, starID int64) (int64, error)
|
||||
|
||||
// CountByOwnerAndActivityType 统计用户指定活动类型的活动藏品数量
|
||||
CountByOwnerAndActivityType(ownerUID, starID int64, activityType string) (int64, error)
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
IncrementLikeCount(id int64) error
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
DecrementLikeCount(id int64) error
|
||||
}
|
||||
|
||||
// activityAssetService 活动藏品服务实现
|
||||
type activityAssetService struct {
|
||||
activityRepo repository.ActivityAssetRepository
|
||||
registryRepo repository.AssetRegistryRepository
|
||||
}
|
||||
|
||||
// NewActivityAssetService 创建活动藏品服务实例
|
||||
func NewActivityAssetService(
|
||||
activityRepo repository.ActivityAssetRepository,
|
||||
registryRepo repository.AssetRegistryRepository,
|
||||
) ActivityAssetService {
|
||||
return &activityAssetService{
|
||||
activityRepo: activityRepo,
|
||||
registryRepo: registryRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建活动藏品
|
||||
func (s *activityAssetService) Create(asset *models.ActivityAsset) error {
|
||||
if asset == nil {
|
||||
return appErrors.ErrInvalidAssetStatus
|
||||
}
|
||||
|
||||
// 创建活动藏品记录
|
||||
if err := s.activityRepo.Create(asset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 同步写入 asset_registry
|
||||
registry := &models.AssetRegistry{
|
||||
AssetID: asset.AssetID,
|
||||
AssetType: models.AssetTypeActivity,
|
||||
OwnerUID: asset.OwnerUID,
|
||||
StarID: asset.StarID,
|
||||
ActivityID: &asset.ActivityID,
|
||||
ActivityType: &asset.ActivityType,
|
||||
Status: asset.Status,
|
||||
LikeCount: asset.LikeCount,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
return s.registryRepo.Create(registry)
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取
|
||||
func (s *activityAssetService) GetByID(id int64) (*models.ActivityAsset, error) {
|
||||
return s.activityRepo.GetByID(id)
|
||||
}
|
||||
|
||||
// GetByAssetID 根据asset_id获取
|
||||
func (s *activityAssetService) GetByAssetID(assetID int64) (*models.ActivityAsset, error) {
|
||||
return s.activityRepo.GetByAssetID(assetID)
|
||||
}
|
||||
|
||||
// GetByOwner 获取用户的活动藏品
|
||||
func (s *activityAssetService) GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.ActivityAsset, error) {
|
||||
return s.activityRepo.GetByOwner(ownerUID, starID, limit, offset)
|
||||
}
|
||||
|
||||
// GetByOwnerAndActivityType 获取用户指定活动类型的活动藏品
|
||||
func (s *activityAssetService) GetByOwnerAndActivityType(ownerUID, starID int64, activityType string, limit, offset int) ([]*models.ActivityAsset, error) {
|
||||
return s.activityRepo.GetByOwnerAndActivityType(ownerUID, starID, activityType, limit, offset)
|
||||
}
|
||||
|
||||
// GetByOwnerAndActivityID 获取用户指定活动的活动藏品
|
||||
func (s *activityAssetService) GetByOwnerAndActivityID(ownerUID, starID int64, activityID int64, limit, offset int) ([]*models.ActivityAsset, error) {
|
||||
return s.activityRepo.GetByOwnerAndActivityID(ownerUID, starID, activityID, limit, offset)
|
||||
}
|
||||
|
||||
// CountByOwner 统计用户的活动藏品数量
|
||||
func (s *activityAssetService) CountByOwner(ownerUID, starID int64) (int64, error) {
|
||||
return s.activityRepo.CountByOwner(ownerUID, starID)
|
||||
}
|
||||
|
||||
// CountByOwnerAndActivityType 统计用户指定活动类型的活动藏品数量
|
||||
func (s *activityAssetService) CountByOwnerAndActivityType(ownerUID, starID int64, activityType string) (int64, error) {
|
||||
return s.activityRepo.CountByOwnerAndActivityType(ownerUID, starID, activityType)
|
||||
}
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
func (s *activityAssetService) IncrementLikeCount(id int64) error {
|
||||
return s.activityRepo.IncrementLikeCount(id)
|
||||
}
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
func (s *activityAssetService) DecrementLikeCount(id int64) error {
|
||||
return s.activityRepo.DecrementLikeCount(id)
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
"github.com/topfans/backend/services/starbookService/repository"
|
||||
)
|
||||
|
||||
// CollectionService 典藏服务接口
|
||||
type CollectionService interface {
|
||||
// Create 创建典藏藏品
|
||||
Create(asset *models.CollectionAsset) error
|
||||
|
||||
// GetByID 根据ID获取
|
||||
GetByID(id int64) (*models.CollectionAsset, error)
|
||||
|
||||
// GetByAssetID 根据asset_id获取
|
||||
GetByAssetID(assetID int64) (*models.CollectionAsset, error)
|
||||
|
||||
// GetByOwner 获取用户的典藏藏品
|
||||
GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.CollectionAsset, error)
|
||||
|
||||
// GetByOwnerAndCategory 获取用户指定分类的典藏藏品
|
||||
GetByOwnerAndCategory(ownerUID, starID int64, category string, limit, offset int) ([]*models.CollectionAsset, error)
|
||||
|
||||
// CountByOwner 统计用户的典藏藏品数量
|
||||
CountByOwner(ownerUID, starID int64) (int64, error)
|
||||
|
||||
// CountByOwnerAndCategory 统计用户指定分类的典藏藏品数量
|
||||
CountByOwnerAndCategory(ownerUID, starID int64, category string) (int64, error)
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
IncrementLikeCount(id int64) error
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
DecrementLikeCount(id int64) error
|
||||
}
|
||||
|
||||
// collectionService 典藏服务实现
|
||||
type collectionService struct {
|
||||
collectionRepo repository.CollectionRepository
|
||||
registryRepo repository.AssetRegistryRepository
|
||||
}
|
||||
|
||||
// NewCollectionService 创建典藏服务实例
|
||||
func NewCollectionService(
|
||||
collectionRepo repository.CollectionRepository,
|
||||
registryRepo repository.AssetRegistryRepository,
|
||||
) CollectionService {
|
||||
return &collectionService{
|
||||
collectionRepo: collectionRepo,
|
||||
registryRepo: registryRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建典藏藏品
|
||||
func (s *collectionService) Create(asset *models.CollectionAsset) error {
|
||||
if asset == nil {
|
||||
return appErrors.ErrInvalidAssetStatus
|
||||
}
|
||||
|
||||
// 创建典藏藏品记录
|
||||
if err := s.collectionRepo.Create(asset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 同步写入 asset_registry
|
||||
registry := &models.AssetRegistry{
|
||||
AssetID: asset.AssetID,
|
||||
AssetType: models.AssetTypeCollection,
|
||||
OwnerUID: asset.OwnerUID,
|
||||
StarID: asset.StarID,
|
||||
CollectionCategory: &asset.Category,
|
||||
Status: asset.Status,
|
||||
LikeCount: asset.LikeCount,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
return s.registryRepo.Create(registry)
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取
|
||||
func (s *collectionService) GetByID(id int64) (*models.CollectionAsset, error) {
|
||||
return s.collectionRepo.GetByID(id)
|
||||
}
|
||||
|
||||
// GetByAssetID 根据asset_id获取
|
||||
func (s *collectionService) GetByAssetID(assetID int64) (*models.CollectionAsset, error) {
|
||||
return s.collectionRepo.GetByAssetID(assetID)
|
||||
}
|
||||
|
||||
// GetByOwner 获取用户的典藏藏品
|
||||
func (s *collectionService) GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.CollectionAsset, error) {
|
||||
return s.collectionRepo.GetByOwner(ownerUID, starID, limit, offset)
|
||||
}
|
||||
|
||||
// GetByOwnerAndCategory 获取用户指定分类的典藏藏品
|
||||
func (s *collectionService) GetByOwnerAndCategory(ownerUID, starID int64, category string, limit, offset int) ([]*models.CollectionAsset, error) {
|
||||
return s.collectionRepo.GetByOwnerAndCategory(ownerUID, starID, category, limit, offset)
|
||||
}
|
||||
|
||||
// CountByOwner 统计用户的典藏藏品数量
|
||||
func (s *collectionService) CountByOwner(ownerUID, starID int64) (int64, error) {
|
||||
return s.collectionRepo.CountByOwner(ownerUID, starID)
|
||||
}
|
||||
|
||||
// CountByOwnerAndCategory 统计用户指定分类的典藏藏品数量
|
||||
func (s *collectionService) CountByOwnerAndCategory(ownerUID, starID int64, category string) (int64, error) {
|
||||
return s.collectionRepo.CountByOwnerAndCategory(ownerUID, starID, category)
|
||||
}
|
||||
|
||||
// IncrementLikeCount 增加点赞数
|
||||
func (s *collectionService) IncrementLikeCount(id int64) error {
|
||||
return s.collectionRepo.IncrementLikeCount(id)
|
||||
}
|
||||
|
||||
// DecrementLikeCount 减少点赞数
|
||||
func (s *collectionService) DecrementLikeCount(id int64) error {
|
||||
return s.collectionRepo.DecrementLikeCount(id)
|
||||
}
|
||||
@ -1,448 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
appErrors "github.com/topfans/backend/pkg/errors"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
pb "github.com/topfans/backend/pkg/proto/starbook"
|
||||
assetRepo "github.com/topfans/backend/services/assetService/repository"
|
||||
starbookRepo "github.com/topfans/backend/services/starbookService/repository"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// StarbookService 星册服务接口
|
||||
type StarbookService interface {
|
||||
// GetStarbookHome 获取星册首页数据
|
||||
GetStarbookHome(ownerUID, starID int64) (*pb.GetStarbookHomeResponse, error)
|
||||
|
||||
// GetStarbookItems 获取星册藏品列表(分页)
|
||||
GetStarbookItems(req *pb.GetStarbookItemsRequest, ownerUID, starID int64) (*pb.GetStarbookItemsResponse, error)
|
||||
}
|
||||
|
||||
// starbookService 星册服务实现
|
||||
type starbookService struct {
|
||||
db *gorm.DB
|
||||
registryRepo starbookRepo.AssetRegistryRepository
|
||||
assetRepo assetRepo.AssetRepository
|
||||
collectionRepo starbookRepo.CollectionRepository
|
||||
activityRepo starbookRepo.ActivityAssetRepository
|
||||
}
|
||||
|
||||
// NewStarbookService 创建星册服务实例
|
||||
func NewStarbookService(
|
||||
db *gorm.DB,
|
||||
registryRepo starbookRepo.AssetRegistryRepository,
|
||||
assetRepo assetRepo.AssetRepository,
|
||||
collectionRepo starbookRepo.CollectionRepository,
|
||||
activityRepo starbookRepo.ActivityAssetRepository,
|
||||
) StarbookService {
|
||||
return &starbookService{
|
||||
db: db,
|
||||
registryRepo: registryRepo,
|
||||
assetRepo: assetRepo,
|
||||
collectionRepo: collectionRepo,
|
||||
activityRepo: activityRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// 常量
|
||||
const (
|
||||
HomePageSize = 3 // 首页每组最多显示数量
|
||||
CastloveCategory = "castlove"
|
||||
CategoryNameRegular = "原创"
|
||||
CategoryNameCollection = "典藏"
|
||||
CategoryNameActivity = "活动"
|
||||
)
|
||||
|
||||
// GetStarbookHome 获取星册首页数据
|
||||
func (s *starbookService) GetStarbookHome(ownerUID, starID int64) (*pb.GetStarbookHomeResponse, error) {
|
||||
// 1. 查询所有索引记录
|
||||
registries, err := s.registryRepo.GetByOwner(ownerUID, starID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2. 按 type 分组
|
||||
typeGroups := make(map[string][]*models.AssetRegistry)
|
||||
for _, reg := range registries {
|
||||
typeGroups[reg.AssetType] = append(typeGroups[reg.AssetType], reg)
|
||||
}
|
||||
|
||||
// 3. 构建响应
|
||||
groups := make([]*pb.AssetGroup, 0)
|
||||
|
||||
// 处理原创藏品 (regular)
|
||||
if regs, ok := typeGroups[models.AssetTypeRegular]; ok {
|
||||
group := s.buildRegularGroup(ownerUID, starID, regs)
|
||||
if group != nil {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理典藏藏品 (collection)
|
||||
if regs, ok := typeGroups[models.AssetTypeCollection]; ok {
|
||||
group := s.buildCollectionGroup(ownerUID, starID, regs)
|
||||
if group != nil {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理活动藏品 (activity)
|
||||
if regs, ok := typeGroups[models.AssetTypeActivity]; ok {
|
||||
group := s.buildActivityGroup(ownerUID, starID, regs)
|
||||
if group != nil {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.GetStarbookHomeResponse{
|
||||
Data: &pb.StarbookHomeData{
|
||||
Groups: groups,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildRegularGroup 构建原创藏品分组
|
||||
func (s *starbookService) buildRegularGroup(ownerUID, starID int64, registries []*models.AssetRegistry) *pb.AssetGroup {
|
||||
// 按 grade 分组
|
||||
gradeGroups := make(map[int32][]*models.AssetRegistry)
|
||||
for _, reg := range registries {
|
||||
if reg.Grade != nil {
|
||||
gradeGroups[*reg.Grade] = append(gradeGroups[*reg.Grade], reg)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 GradeSection
|
||||
grades := make([]*pb.GradeSection, 0)
|
||||
for grade, regs := range gradeGroups {
|
||||
// 排序:按点赞数降序,保留前3
|
||||
sort.Slice(regs, func(i, j int) bool {
|
||||
return regs[i].LikeCount > regs[j].LikeCount
|
||||
})
|
||||
|
||||
// 截取前3条(点赞数最高的3个)
|
||||
displayRegs := regs
|
||||
hasMore := false
|
||||
if len(regs) > HomePageSize {
|
||||
displayRegs = regs[:HomePageSize]
|
||||
hasMore = true
|
||||
}
|
||||
|
||||
// 获取资产详情并生成预签名URL
|
||||
items := s.buildAssetItemsFromRegistries(displayRegs, models.AssetTypeRegular)
|
||||
|
||||
gradeSection := &pb.GradeSection{
|
||||
Grade: grade,
|
||||
Items: items,
|
||||
TotalCount: int32(len(regs)),
|
||||
HasMore: hasMore,
|
||||
}
|
||||
grades = append(grades, gradeSection)
|
||||
}
|
||||
|
||||
// 按 grade 降序排序
|
||||
sort.Slice(grades, func(i, j int) bool {
|
||||
return grades[i].Grade > grades[j].Grade
|
||||
})
|
||||
|
||||
// 计算 total_count 和 has_more
|
||||
totalCount := int32(0)
|
||||
hasMore := false
|
||||
for _, g := range grades {
|
||||
totalCount += g.TotalCount
|
||||
if g.HasMore {
|
||||
hasMore = true
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.AssetGroup{
|
||||
Type: models.AssetTypeRegular,
|
||||
Category: CastloveCategory,
|
||||
CategoryName: CategoryNameRegular,
|
||||
Grades: grades,
|
||||
TotalCount: totalCount,
|
||||
HasMore: hasMore,
|
||||
}
|
||||
}
|
||||
|
||||
// buildCollectionGroup 构建典藏藏品分组
|
||||
func (s *starbookService) buildCollectionGroup(ownerUID, starID int64, registries []*models.AssetRegistry) *pb.AssetGroup {
|
||||
// 按 category 分组
|
||||
categoryGroups := make(map[string][]*models.AssetRegistry)
|
||||
for _, reg := range registries {
|
||||
if reg.CollectionCategory != nil && *reg.CollectionCategory != "" {
|
||||
categoryGroups[*reg.CollectionCategory] = append(categoryGroups[*reg.CollectionCategory], reg)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 AssetGroup items
|
||||
allItems := make([]*pb.AssetItem, 0)
|
||||
for category, regs := range categoryGroups {
|
||||
// 排序:按创建时间降序
|
||||
sort.Slice(regs, func(i, j int) bool {
|
||||
return regs[i].CreatedAt > regs[j].CreatedAt
|
||||
})
|
||||
|
||||
// 截取前3条(点赞数最高的3个)
|
||||
displayRegs := regs
|
||||
if len(regs) > HomePageSize {
|
||||
displayRegs = regs[:HomePageSize]
|
||||
}
|
||||
|
||||
// 获取资产详情
|
||||
items := s.buildAssetItemsFromRegistries(displayRegs, models.AssetTypeCollection)
|
||||
for _, item := range items {
|
||||
item.Category = category
|
||||
}
|
||||
allItems = append(allItems, items...)
|
||||
}
|
||||
|
||||
// 计算 total_count 和 has_more
|
||||
totalCount := int32(len(registries))
|
||||
hasMore := len(registries) > HomePageSize
|
||||
|
||||
return &pb.AssetGroup{
|
||||
Type: models.AssetTypeCollection,
|
||||
Category: "",
|
||||
CategoryName: CategoryNameCollection,
|
||||
Items: allItems,
|
||||
TotalCount: totalCount,
|
||||
HasMore: hasMore,
|
||||
}
|
||||
}
|
||||
|
||||
// buildActivityGroup 构建活动藏品分组
|
||||
func (s *starbookService) buildActivityGroup(ownerUID, starID int64, registries []*models.AssetRegistry) *pb.AssetGroup {
|
||||
// 按 activity_type 分组
|
||||
typeGroups := make(map[string][]*models.AssetRegistry)
|
||||
for _, reg := range registries {
|
||||
if reg.ActivityType != nil && *reg.ActivityType != "" {
|
||||
typeGroups[*reg.ActivityType] = append(typeGroups[*reg.ActivityType], reg)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 AssetGroup items
|
||||
allItems := make([]*pb.AssetItem, 0)
|
||||
for activityType, regs := range typeGroups {
|
||||
// 排序:按创建时间降序
|
||||
sort.Slice(regs, func(i, j int) bool {
|
||||
return regs[i].CreatedAt > regs[j].CreatedAt
|
||||
})
|
||||
|
||||
// 截取前3条(点赞数最高的3个)
|
||||
displayRegs := regs
|
||||
if len(regs) > HomePageSize {
|
||||
displayRegs = regs[:HomePageSize]
|
||||
}
|
||||
|
||||
// 获取资产详情
|
||||
items := s.buildAssetItemsFromRegistries(displayRegs, models.AssetTypeActivity)
|
||||
for _, item := range items {
|
||||
item.Category = activityType
|
||||
}
|
||||
allItems = append(allItems, items...)
|
||||
}
|
||||
|
||||
// 计算 total_count 和 has_more
|
||||
totalCount := int32(len(registries))
|
||||
hasMore := len(registries) > HomePageSize
|
||||
|
||||
return &pb.AssetGroup{
|
||||
Type: models.AssetTypeActivity,
|
||||
Category: "",
|
||||
CategoryName: CategoryNameActivity,
|
||||
Items: allItems,
|
||||
TotalCount: totalCount,
|
||||
HasMore: hasMore,
|
||||
}
|
||||
}
|
||||
|
||||
// buildAssetItemsFromRegistries 从索引记录构建资产项(使用批量查询优化)
|
||||
func (s *starbookService) buildAssetItemsFromRegistries(registries []*models.AssetRegistry, assetType string) []*pb.AssetItem {
|
||||
if len(registries) == 0 {
|
||||
return []*pb.AssetItem{}
|
||||
}
|
||||
|
||||
items := make([]*pb.AssetItem, 0, len(registries))
|
||||
|
||||
// 收集所有 asset IDs
|
||||
assetIDs := make([]int64, 0, len(registries))
|
||||
for _, reg := range registries {
|
||||
assetIDs = append(assetIDs, reg.AssetID)
|
||||
}
|
||||
|
||||
// 批量查询资产信息(替代 N+1 查询)
|
||||
var assetCoverMap map[int64]string // assetID -> coverURL
|
||||
var assetNameMap map[int64]string // assetID -> name
|
||||
var categoryMap map[int64]string // assetID -> category
|
||||
var assetLikeCountMap map[int64]int32 // assetID -> likeCount
|
||||
|
||||
switch assetType {
|
||||
case models.AssetTypeRegular:
|
||||
assets, err := s.assetRepo.GetByIDs(assetIDs)
|
||||
if err == nil && len(assets) > 0 {
|
||||
assetCoverMap = make(map[int64]string)
|
||||
assetNameMap = make(map[int64]string)
|
||||
assetLikeCountMap = make(map[int64]int32)
|
||||
for _, asset := range assets {
|
||||
assetCoverMap[asset.ID] = asset.CoverURL
|
||||
assetNameMap[asset.ID] = asset.Name
|
||||
assetLikeCountMap[asset.ID] = asset.LikeCount
|
||||
}
|
||||
}
|
||||
case models.AssetTypeCollection:
|
||||
colAssets, err := s.collectionRepo.GetByAssetIDs(assetIDs)
|
||||
if err == nil && len(colAssets) > 0 {
|
||||
assetCoverMap = make(map[int64]string)
|
||||
assetNameMap = make(map[int64]string)
|
||||
categoryMap = make(map[int64]string)
|
||||
for _, colAsset := range colAssets {
|
||||
assetCoverMap[colAsset.AssetID] = colAsset.CoverURL
|
||||
assetNameMap[colAsset.AssetID] = colAsset.Name
|
||||
if colAsset.Category != "" {
|
||||
categoryMap[colAsset.AssetID] = colAsset.Category
|
||||
}
|
||||
}
|
||||
}
|
||||
case models.AssetTypeActivity:
|
||||
actAssets, err := s.activityRepo.GetByAssetIDs(assetIDs)
|
||||
if err == nil && len(actAssets) > 0 {
|
||||
assetCoverMap = make(map[int64]string)
|
||||
assetNameMap = make(map[int64]string)
|
||||
categoryMap = make(map[int64]string)
|
||||
for _, actAsset := range actAssets {
|
||||
assetCoverMap[actAsset.AssetID] = actAsset.CoverURL
|
||||
assetNameMap[actAsset.AssetID] = actAsset.Name
|
||||
if actAsset.ActivityType != "" {
|
||||
categoryMap[actAsset.AssetID] = actAsset.ActivityType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 items
|
||||
for _, reg := range registries {
|
||||
item := &pb.AssetItem{
|
||||
AssetId: reg.AssetID,
|
||||
LikeCount: reg.LikeCount,
|
||||
CreatedAt: reg.CreatedAt,
|
||||
Category: CastloveCategory,
|
||||
Grade: 0,
|
||||
DisplayStatus: reg.DisplayStatus,
|
||||
}
|
||||
|
||||
// grade 处理
|
||||
if assetType == models.AssetTypeRegular && reg.Grade != nil {
|
||||
item.Grade = *reg.Grade
|
||||
}
|
||||
|
||||
// 填充资产信息
|
||||
if name, ok := assetNameMap[reg.AssetID]; ok {
|
||||
item.Name = name
|
||||
}
|
||||
if coverURL, ok := assetCoverMap[reg.AssetID]; ok && coverURL != "" {
|
||||
item.CoverUrlSigned = coverURL
|
||||
}
|
||||
if cat, ok := categoryMap[reg.AssetID]; ok {
|
||||
item.Category = cat
|
||||
}
|
||||
|
||||
// 从 assets 表获取点赞数(regular 类型)
|
||||
if assetType == models.AssetTypeRegular {
|
||||
if likeCount, ok := assetLikeCountMap[reg.AssetID]; ok {
|
||||
item.LikeCount = likeCount
|
||||
}
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
// GetStarbookItems 获取星册藏品列表(分页)
|
||||
func (s *starbookService) GetStarbookItems(req *pb.GetStarbookItemsRequest, ownerUID, starID int64) (*pb.GetStarbookItemsResponse, error) {
|
||||
// 参数验证
|
||||
if req.Type == "" {
|
||||
return nil, appErrors.ErrInvalidAssetType
|
||||
}
|
||||
|
||||
assetType := req.Type
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
var registries []*models.AssetRegistry
|
||||
var totalCount int64
|
||||
var err error
|
||||
|
||||
switch assetType {
|
||||
case models.AssetTypeRegular:
|
||||
grade := req.Grade
|
||||
if grade <= 0 {
|
||||
grade = 1
|
||||
}
|
||||
registries, err = s.registryRepo.GetByOwnerAndTypeAndGrade(ownerUID, starID, assetType, grade, int(pageSize), int(offset))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount, err = s.registryRepo.CountByOwnerAndTypeAndGrade(ownerUID, starID, assetType, grade)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case models.AssetTypeCollection:
|
||||
category := req.Category
|
||||
if category == "" {
|
||||
category = CastloveCategory
|
||||
}
|
||||
registries, err = s.registryRepo.GetByOwnerAndTypeAndCategory(ownerUID, starID, assetType, category, int(pageSize), int(offset))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount, err = s.registryRepo.CountByOwnerAndTypeAndCategory(ownerUID, starID, assetType, category)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case models.AssetTypeActivity:
|
||||
category := req.Category
|
||||
if category == "" {
|
||||
category = CastloveCategory
|
||||
}
|
||||
registries, err = s.registryRepo.GetByOwnerAndTypeAndCategory(ownerUID, starID, assetType, category, int(pageSize), int(offset))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount, err = s.registryRepo.CountByOwnerAndTypeAndCategory(ownerUID, starID, assetType, category)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, appErrors.ErrInvalidAssetType
|
||||
}
|
||||
|
||||
// 构建 items
|
||||
items := s.buildAssetItemsFromRegistries(registries, assetType)
|
||||
|
||||
hasMore := int64(page*pageSize) < totalCount
|
||||
|
||||
return &pb.GetStarbookItemsResponse{
|
||||
Data: &pb.AssetListData{
|
||||
Items: items,
|
||||
Total: totalCount,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
HasMore: hasMore,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user