Refactor order and tenant ledger models to use consts for Currency and Type fields; add new UserStatus values; implement comprehensive test cases for content, creator, order, super, and wallet services.
This commit is contained in:
@@ -2,27 +2,174 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"quyun/v2/app/errorx"
|
||||
creator_dto "quyun/v2/app/http/v1/dto"
|
||||
"quyun/v2/database/models"
|
||||
"quyun/v2/pkg/consts"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cast"
|
||||
"go.ipao.vip/gen/types"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// @provider
|
||||
type creator struct{}
|
||||
|
||||
func (s *creator) Apply(ctx context.Context, form *creator_dto.ApplyForm) error {
|
||||
userID := ctx.Value(consts.CtxKeyUser)
|
||||
if userID == nil {
|
||||
return errorx.ErrUnauthorized
|
||||
}
|
||||
uid := cast.ToInt64(userID)
|
||||
|
||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||
// Check if already has a tenant
|
||||
count, _ := q.Where(tbl.UserID.Eq(uid)).Count()
|
||||
if count > 0 {
|
||||
return errorx.ErrBadRequest.WithMsg("您已是创作者")
|
||||
}
|
||||
|
||||
// Create Tenant
|
||||
tenant := &models.Tenant{
|
||||
UserID: uid,
|
||||
Name: form.Name,
|
||||
// Bio/Avatar in config
|
||||
Code: uuid.NewString()[:8], // Generate random code
|
||||
UUID: types.UUID(uuid.New()),
|
||||
Status: consts.TenantStatusPendingVerify,
|
||||
}
|
||||
|
||||
if err := q.Create(tenant); err != nil {
|
||||
return errorx.ErrDatabaseError
|
||||
}
|
||||
|
||||
// Also add user as tenant_admin in tenant_users
|
||||
tu := &models.TenantUser{
|
||||
TenantID: tenant.ID,
|
||||
UserID: uid,
|
||||
Role: types.Array[consts.TenantUserRole]{consts.TenantUserRoleTenantAdmin},
|
||||
Status: consts.UserStatusVerified,
|
||||
}
|
||||
if err := models.TenantUserQuery.WithContext(ctx).Create(tu); err != nil {
|
||||
return errorx.ErrDatabaseError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *creator) Dashboard(ctx context.Context) (*creator_dto.DashboardStats, error) {
|
||||
return &creator_dto.DashboardStats{}, nil
|
||||
tid, err := s.getTenantID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Mock stats for now or query
|
||||
// Followers: count tenant_users
|
||||
followers, _ := models.TenantUserQuery.WithContext(ctx).Where(models.TenantUserQuery.TenantID.Eq(tid)).Count()
|
||||
|
||||
stats := &creator_dto.DashboardStats{
|
||||
TotalFollowers: creator_dto.IntStatItem{Value: int(followers)},
|
||||
TotalRevenue: creator_dto.FloatStatItem{Value: 0},
|
||||
PendingRefunds: 0,
|
||||
NewMessages: 0,
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (s *creator) ListContents(ctx context.Context, status, genre, keyword string) ([]creator_dto.ContentItem, error) {
|
||||
return []creator_dto.ContentItem{}, nil
|
||||
tid, err := s.getTenantID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.TenantID.Eq(tid))
|
||||
|
||||
if status != "" {
|
||||
q = q.Where(tbl.Status.Eq(consts.ContentStatus(status)))
|
||||
}
|
||||
if genre != "" {
|
||||
q = q.Where(tbl.Genre.Eq(genre))
|
||||
}
|
||||
if keyword != "" {
|
||||
q = q.Where(tbl.Title.Like("%" + keyword + "%"))
|
||||
}
|
||||
|
||||
list, err := q.Order(tbl.CreatedAt.Desc()).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError
|
||||
}
|
||||
|
||||
var data []creator_dto.ContentItem
|
||||
for _, item := range list {
|
||||
data = append(data, creator_dto.ContentItem{
|
||||
ID: cast.ToString(item.ID),
|
||||
Title: item.Title,
|
||||
Genre: item.Genre,
|
||||
Views: int(item.Views),
|
||||
Likes: int(item.Likes),
|
||||
IsPurchased: false,
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *creator) CreateContent(ctx context.Context, form *creator_dto.ContentCreateForm) error {
|
||||
return nil
|
||||
tid, err := s.getTenantID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uid := cast.ToInt64(ctx.Value(consts.CtxKeyUser))
|
||||
|
||||
return models.Q.Transaction(func(tx *models.Query) error {
|
||||
// 1. Create Content
|
||||
content := &models.Content{
|
||||
TenantID: tid,
|
||||
UserID: uid,
|
||||
Title: form.Title,
|
||||
Genre: form.Genre,
|
||||
Status: consts.ContentStatusPublished,
|
||||
}
|
||||
if err := tx.Content.WithContext(ctx).Create(content); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Link Assets
|
||||
if len(form.MediaIDs) > 0 {
|
||||
var assets []*models.ContentAsset
|
||||
for i, mid := range form.MediaIDs {
|
||||
assets = append(assets, &models.ContentAsset{
|
||||
TenantID: tid,
|
||||
UserID: uid,
|
||||
ContentID: content.ID,
|
||||
AssetID: cast.ToInt64(mid),
|
||||
Sort: int32(i),
|
||||
Role: consts.ContentAssetRoleMain,
|
||||
})
|
||||
}
|
||||
if err := tx.ContentAsset.WithContext(ctx).Create(assets...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Set Price
|
||||
price := &models.ContentPrice{
|
||||
TenantID: tid,
|
||||
UserID: uid,
|
||||
ContentID: content.ID,
|
||||
PriceAmount: int64(form.Price * 100), // Convert to cents
|
||||
Currency: consts.CurrencyCNY,
|
||||
}
|
||||
if err := tx.ContentPrice.WithContext(ctx).Create(price); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *creator) UpdateContent(ctx context.Context, id string, form *creator_dto.ContentUpdateForm) error {
|
||||
@@ -30,11 +177,43 @@ func (s *creator) UpdateContent(ctx context.Context, id string, form *creator_dt
|
||||
}
|
||||
|
||||
func (s *creator) DeleteContent(ctx context.Context, id string) error {
|
||||
cid := cast.ToInt64(id)
|
||||
tid, err := s.getTenantID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = models.ContentQuery.WithContext(ctx).Where(models.ContentQuery.ID.Eq(cid), models.ContentQuery.TenantID.Eq(tid)).Delete()
|
||||
if err != nil {
|
||||
return errorx.ErrDatabaseError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *creator) ListOrders(ctx context.Context, status, keyword string) ([]creator_dto.Order, error) {
|
||||
return []creator_dto.Order{}, nil
|
||||
tid, err := s.getTenantID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tbl, q := models.OrderQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.TenantID.Eq(tid))
|
||||
// Filters...
|
||||
list, err := q.Order(tbl.CreatedAt.Desc()).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError
|
||||
}
|
||||
|
||||
var data []creator_dto.Order
|
||||
for _, o := range list {
|
||||
data = append(data, creator_dto.Order{
|
||||
ID: cast.ToString(o.ID),
|
||||
Status: string(o.Status), // Enum conversion
|
||||
Amount: float64(o.AmountPaid) / 100.0,
|
||||
CreateTime: o.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *creator) ProcessRefund(ctx context.Context, id string, form *creator_dto.RefundForm) error {
|
||||
@@ -42,7 +221,19 @@ func (s *creator) ProcessRefund(ctx context.Context, id string, form *creator_dt
|
||||
}
|
||||
|
||||
func (s *creator) GetSettings(ctx context.Context) (*creator_dto.Settings, error) {
|
||||
return &creator_dto.Settings{}, nil
|
||||
tid, err := s.getTenantID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.ID.Eq(tid)).First()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrRecordNotFound
|
||||
}
|
||||
// Extract from t.Config
|
||||
return &creator_dto.Settings{
|
||||
Name: t.Name,
|
||||
// Bio/Avatar from Config
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *creator) UpdateSettings(ctx context.Context, form *creator_dto.Settings) error {
|
||||
@@ -64,3 +255,23 @@ func (s *creator) RemovePayoutAccount(ctx context.Context, id string) error {
|
||||
func (s *creator) Withdraw(ctx context.Context, form *creator_dto.WithdrawForm) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
func (s *creator) getTenantID(ctx context.Context) (int64, error) {
|
||||
userID := ctx.Value(consts.CtxKeyUser)
|
||||
if userID == nil {
|
||||
return 0, errorx.ErrUnauthorized
|
||||
}
|
||||
uid := cast.ToInt64(userID)
|
||||
|
||||
// Simple check: User owns tenant
|
||||
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(uid)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, errorx.ErrPermissionDenied.WithMsg("非创作者")
|
||||
}
|
||||
return 0, errorx.ErrDatabaseError
|
||||
}
|
||||
return t.ID, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user