f
This commit is contained in:
@@ -269,6 +269,9 @@ func (a *Application) autoMigrate(db *gorm.DB) error {
|
|||||||
&subordinateEntities.SubordinateInvitation{},
|
&subordinateEntities.SubordinateInvitation{},
|
||||||
&subordinateEntities.UserSubordinateLink{},
|
&subordinateEntities.UserSubordinateLink{},
|
||||||
&subordinateEntities.SubordinateWalletAllocation{},
|
&subordinateEntities.SubordinateWalletAllocation{},
|
||||||
|
&subordinateEntities.SubordinateQuotaPurchase{},
|
||||||
|
&subordinateEntities.UserProductQuotaAccount{},
|
||||||
|
&subordinateEntities.UserProductQuotaLedger{},
|
||||||
|
|
||||||
// 任务域
|
// 任务域
|
||||||
&taskEntities.AsyncTask{},
|
&taskEntities.AsyncTask{},
|
||||||
|
|||||||
@@ -53,3 +53,26 @@ type RemoveChildSubscriptionCommand struct {
|
|||||||
ChildUserID string `json:"child_user_id" binding:"required"`
|
ChildUserID string `json:"child_user_id" binding:"required"`
|
||||||
SubscriptionID string `json:"subscription_id" binding:"required"`
|
SubscriptionID string `json:"subscription_id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PurchaseChildQuotaCommand 主账号为子账号购买调用额度
|
||||||
|
type PurchaseChildQuotaCommand struct {
|
||||||
|
ParentUserID string
|
||||||
|
ChildUserID string `json:"child_user_id" binding:"required"`
|
||||||
|
ProductID string `json:"product_id" binding:"required"`
|
||||||
|
CallCount int64 `json:"call_count" binding:"required,min=1"`
|
||||||
|
VerifyCode string `json:"verify_code" binding:"required,len=6"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListChildQuotaPurchasesCommand 下属额度购买记录查询
|
||||||
|
type ListChildQuotaPurchasesCommand struct {
|
||||||
|
ParentUserID string
|
||||||
|
ChildUserID string `json:"child_user_id" form:"child_user_id" binding:"required"`
|
||||||
|
Page int `json:"page" form:"page"`
|
||||||
|
PageSize int `json:"page_size" form:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListChildQuotaAccountsCommand 下属额度账户查询
|
||||||
|
type ListChildQuotaAccountsCommand struct {
|
||||||
|
ParentUserID string
|
||||||
|
ChildUserID string `json:"child_user_id" form:"child_user_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,3 +55,28 @@ type ChildSubscriptionItem struct {
|
|||||||
UIComponentPrice string `json:"ui_component_price"`
|
UIComponentPrice string `json:"ui_component_price"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChildQuotaPurchaseItem 下属额度购买记录
|
||||||
|
type ChildQuotaPurchaseItem struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
ProductID string `json:"product_id"`
|
||||||
|
CallCount int64 `json:"call_count"`
|
||||||
|
UnitPrice string `json:"unit_price"`
|
||||||
|
TotalAmount string `json:"total_amount"`
|
||||||
|
BusinessRef string `json:"business_ref"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildQuotaPurchaseListResponse 下属额度购买记录列表
|
||||||
|
type ChildQuotaPurchaseListResponse struct {
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Items []ChildQuotaPurchaseItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildQuotaAccountItem 下属产品额度账户
|
||||||
|
type ChildQuotaAccountItem struct {
|
||||||
|
ProductID string `json:"product_id"`
|
||||||
|
TotalQuota int64 `json:"total_quota"`
|
||||||
|
UsedQuota int64 `json:"used_quota"`
|
||||||
|
AvailableQuota int64 `json:"available_quota"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,4 +17,8 @@ type SubordinateApplicationService interface {
|
|||||||
AssignChildSubscription(ctx context.Context, cmd *commands.AssignChildSubscriptionCommand) error
|
AssignChildSubscription(ctx context.Context, cmd *commands.AssignChildSubscriptionCommand) error
|
||||||
ListChildSubscriptions(ctx context.Context, cmd *commands.ListChildSubscriptionsCommand) ([]responses.ChildSubscriptionItem, error)
|
ListChildSubscriptions(ctx context.Context, cmd *commands.ListChildSubscriptionsCommand) ([]responses.ChildSubscriptionItem, error)
|
||||||
RemoveChildSubscription(ctx context.Context, cmd *commands.RemoveChildSubscriptionCommand) error
|
RemoveChildSubscription(ctx context.Context, cmd *commands.RemoveChildSubscriptionCommand) error
|
||||||
|
PurchaseChildQuota(ctx context.Context, cmd *commands.PurchaseChildQuotaCommand) error
|
||||||
|
ListChildQuotaPurchases(ctx context.Context, cmd *commands.ListChildQuotaPurchasesCommand) (*responses.ChildQuotaPurchaseListResponse, error)
|
||||||
|
ListChildQuotaAccounts(ctx context.Context, cmd *commands.ListChildQuotaAccountsCommand) ([]responses.ChildQuotaAccountItem, error)
|
||||||
|
ListMyQuotaAccounts(ctx context.Context, userID string) ([]responses.ChildQuotaAccountItem, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,3 +391,218 @@ func (s *SubordinateApplicationServiceImpl) RemoveChildSubscription(ctx context.
|
|||||||
}
|
}
|
||||||
return s.productSub.CancelSubscription(ctx, cmd.SubscriptionID)
|
return s.productSub.CancelSubscription(ctx, cmd.SubscriptionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PurchaseChildQuota 主账号为子账号购买调用额度(按子账号订阅价结算)
|
||||||
|
func (s *SubordinateApplicationServiceImpl) PurchaseChildQuota(ctx context.Context, cmd *commands.PurchaseChildQuotaCommand) error {
|
||||||
|
if cmd.CallCount <= 0 {
|
||||||
|
return fmt.Errorf("购买次数必须大于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
parentUser, err := s.userRepo.GetByID(ctx, cmd.ParentUserID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("主账号信息获取失败")
|
||||||
|
}
|
||||||
|
if err := s.smsService.VerifyCode(ctx, parentUser.Phone, strings.TrimSpace(cmd.VerifyCode), user_entities.SMSSceneLogin); err != nil {
|
||||||
|
return fmt.Errorf("验证码错误或已过期")
|
||||||
|
}
|
||||||
|
|
||||||
|
lnk, err := s.subRepo.FindLinkByParentAndChild(ctx, cmd.ParentUserID, cmd.ChildUserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if lnk == nil || lnk.Status != subentities.LinkStatusActive {
|
||||||
|
return fmt.Errorf("该用户不是您的有效下属")
|
||||||
|
}
|
||||||
|
|
||||||
|
parentSub, err := s.productSub.GetUserSubscribedProduct(ctx, cmd.ParentUserID, cmd.ProductID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if parentSub == nil {
|
||||||
|
return fmt.Errorf("主账号未订阅该产品,无法购买额度")
|
||||||
|
}
|
||||||
|
if !parentSub.Price.GreaterThan(decimal.Zero) {
|
||||||
|
return fmt.Errorf("主账号订阅价格异常,无法购买额度")
|
||||||
|
}
|
||||||
|
|
||||||
|
callCountDec := decimal.NewFromInt(cmd.CallCount)
|
||||||
|
totalAmount := parentSub.Price.Mul(callCountDec)
|
||||||
|
if !totalAmount.GreaterThan(decimal.Zero) {
|
||||||
|
return fmt.Errorf("购买金额必须大于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
bizRef := uuid.New().String()
|
||||||
|
return s.txm.ExecuteInTx(ctx, func(txCtx context.Context) error {
|
||||||
|
// 购买额度前自动确保子账号存在该产品订阅,并统一为主账号订阅价
|
||||||
|
childSub, err := s.productSub.GetUserSubscribedProduct(txCtx, cmd.ChildUserID, cmd.ProductID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if childSub == nil {
|
||||||
|
newSub := &productentities.Subscription{
|
||||||
|
UserID: cmd.ChildUserID,
|
||||||
|
ProductID: cmd.ProductID,
|
||||||
|
Price: parentSub.Price,
|
||||||
|
UIComponentPrice: parentSub.UIComponentPrice,
|
||||||
|
}
|
||||||
|
if err := s.productSub.SaveSubscription(txCtx, newSub); err != nil {
|
||||||
|
return fmt.Errorf("为下属创建订阅失败: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
childSub.Price = parentSub.Price
|
||||||
|
childSub.UIComponentPrice = parentSub.UIComponentPrice
|
||||||
|
if err := s.productSub.SaveSubscription(txCtx, childSub); err != nil {
|
||||||
|
return fmt.Errorf("更新下属订阅失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := s.walletRepo.UpdateBalanceByUserID(txCtx, cmd.ParentUserID, totalAmount, "subtract")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("主账号扣款失败,请重试")
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := s.subRepo.FindQuotaAccount(txCtx, cmd.ChildUserID, cmd.ProductID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var beforeAvailable int64
|
||||||
|
if account == nil {
|
||||||
|
account = &subentities.UserProductQuotaAccount{
|
||||||
|
UserID: cmd.ChildUserID,
|
||||||
|
ProductID: cmd.ProductID,
|
||||||
|
TotalQuota: cmd.CallCount,
|
||||||
|
UsedQuota: 0,
|
||||||
|
AvailableQuota: cmd.CallCount,
|
||||||
|
}
|
||||||
|
beforeAvailable = 0
|
||||||
|
if err := s.subRepo.CreateQuotaAccount(txCtx, account); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
beforeAvailable = account.AvailableQuota
|
||||||
|
account.TotalQuota += cmd.CallCount
|
||||||
|
account.AvailableQuota += cmd.CallCount
|
||||||
|
if err := s.subRepo.UpdateQuotaAccount(txCtx, account); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
purchase := &subentities.SubordinateQuotaPurchase{
|
||||||
|
ParentUserID: cmd.ParentUserID,
|
||||||
|
ChildUserID: cmd.ChildUserID,
|
||||||
|
ProductID: cmd.ProductID,
|
||||||
|
CallCount: cmd.CallCount,
|
||||||
|
UnitPrice: parentSub.Price,
|
||||||
|
TotalAmount: totalAmount,
|
||||||
|
BusinessRef: bizRef,
|
||||||
|
OperatorUserID: cmd.ParentUserID,
|
||||||
|
}
|
||||||
|
if err := s.subRepo.CreateQuotaPurchase(txCtx, purchase); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ledger := &subentities.UserProductQuotaLedger{
|
||||||
|
UserID: cmd.ChildUserID,
|
||||||
|
ProductID: cmd.ProductID,
|
||||||
|
ChangeType: subentities.QuotaLedgerChangeTypePurchaseForSub,
|
||||||
|
DeltaQuota: cmd.CallCount,
|
||||||
|
BeforeQuota: beforeAvailable,
|
||||||
|
AfterQuota: beforeAvailable + cmd.CallCount,
|
||||||
|
SourceID: purchase.ID,
|
||||||
|
OperatorID: cmd.ParentUserID,
|
||||||
|
Remark: "主账号为子账号购买额度",
|
||||||
|
}
|
||||||
|
return s.subRepo.CreateQuotaLedger(txCtx, ledger)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListChildQuotaPurchases 下属额度购买记录
|
||||||
|
func (s *SubordinateApplicationServiceImpl) ListChildQuotaPurchases(ctx context.Context, cmd *commands.ListChildQuotaPurchasesCommand) (*responses.ChildQuotaPurchaseListResponse, error) {
|
||||||
|
lnk, err := s.subRepo.FindLinkByParentAndChild(ctx, cmd.ParentUserID, cmd.ChildUserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if lnk == nil || lnk.Status != subentities.LinkStatusActive {
|
||||||
|
return nil, fmt.Errorf("该用户不是您的有效下属")
|
||||||
|
}
|
||||||
|
|
||||||
|
page := cmd.Page
|
||||||
|
pageSize := cmd.PageSize
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 || pageSize > 100 {
|
||||||
|
pageSize = 20
|
||||||
|
}
|
||||||
|
offset := (page - 1) * pageSize
|
||||||
|
rows, total, err := s.subRepo.ListQuotaPurchasesByParentAndChild(ctx, cmd.ParentUserID, cmd.ChildUserID, pageSize, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]responses.ChildQuotaPurchaseItem, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
items = append(items, responses.ChildQuotaPurchaseItem{
|
||||||
|
ID: row.ID,
|
||||||
|
ProductID: row.ProductID,
|
||||||
|
CallCount: row.CallCount,
|
||||||
|
UnitPrice: row.UnitPrice.StringFixed(2),
|
||||||
|
TotalAmount: row.TotalAmount.StringFixed(2),
|
||||||
|
BusinessRef: row.BusinessRef,
|
||||||
|
CreatedAt: row.CreatedAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &responses.ChildQuotaPurchaseListResponse{
|
||||||
|
Total: total,
|
||||||
|
Items: items,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListChildQuotaAccounts 下属额度账户
|
||||||
|
func (s *SubordinateApplicationServiceImpl) ListChildQuotaAccounts(ctx context.Context, cmd *commands.ListChildQuotaAccountsCommand) ([]responses.ChildQuotaAccountItem, error) {
|
||||||
|
lnk, err := s.subRepo.FindLinkByParentAndChild(ctx, cmd.ParentUserID, cmd.ChildUserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if lnk == nil || lnk.Status != subentities.LinkStatusActive {
|
||||||
|
return nil, fmt.Errorf("该用户不是您的有效下属")
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts, err := s.subRepo.ListQuotaAccountsByUser(ctx, cmd.ChildUserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items := make([]responses.ChildQuotaAccountItem, 0, len(accounts))
|
||||||
|
for _, account := range accounts {
|
||||||
|
items = append(items, responses.ChildQuotaAccountItem{
|
||||||
|
ProductID: account.ProductID,
|
||||||
|
TotalQuota: account.TotalQuota,
|
||||||
|
UsedQuota: account.UsedQuota,
|
||||||
|
AvailableQuota: account.AvailableQuota,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyQuotaAccounts 查询当前用户额度账户(通用能力,适配所有用户)
|
||||||
|
func (s *SubordinateApplicationServiceImpl) ListMyQuotaAccounts(ctx context.Context, userID string) ([]responses.ChildQuotaAccountItem, error) {
|
||||||
|
accounts, err := s.subRepo.ListQuotaAccountsByUser(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items := make([]responses.ChildQuotaAccountItem, 0, len(accounts))
|
||||||
|
for _, account := range accounts {
|
||||||
|
items = append(items, responses.ChildQuotaAccountItem{
|
||||||
|
ProductID: account.ProductID,
|
||||||
|
TotalQuota: account.TotalQuota,
|
||||||
|
UsedQuota: account.UsedQuota,
|
||||||
|
AvailableQuota: account.AvailableQuota,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|||||||
96
internal/domains/subordinate/entities/quota.go
Normal file
96
internal/domains/subordinate/entities/quota.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// QuotaLedgerChangeTypePurchaseForSub 主账号为子账号购买额度
|
||||||
|
QuotaLedgerChangeTypePurchaseForSub = "purchase_for_sub"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubordinateQuotaPurchase 主账号为子账号购买额度记录
|
||||||
|
type SubordinateQuotaPurchase struct {
|
||||||
|
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
|
||||||
|
ParentUserID string `gorm:"type:varchar(36);not null;index" json:"parent_user_id"`
|
||||||
|
ChildUserID string `gorm:"type:varchar(36);not null;index" json:"child_user_id"`
|
||||||
|
ProductID string `gorm:"type:varchar(36);not null;index" json:"product_id"`
|
||||||
|
CallCount int64 `gorm:"type:bigint;not null" json:"call_count"`
|
||||||
|
UnitPrice decimal.Decimal `gorm:"type:decimal(20,8);not null" json:"unit_price"`
|
||||||
|
TotalAmount decimal.Decimal `gorm:"type:decimal(20,8);not null" json:"total_amount"`
|
||||||
|
BusinessRef string `gorm:"type:varchar(64);not null;uniqueIndex" json:"business_ref"`
|
||||||
|
OperatorUserID string `gorm:"type:varchar(36);not null" json:"operator_user_id"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (SubordinateQuotaPurchase) TableName() string {
|
||||||
|
return "subordinate_quota_purchases"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *SubordinateQuotaPurchase) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
if q.ID == "" {
|
||||||
|
q.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserProductQuotaAccount 用户产品额度账户(通用模型,适配所有用户)
|
||||||
|
type UserProductQuotaAccount struct {
|
||||||
|
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
|
||||||
|
UserID string `gorm:"type:varchar(36);not null;index:idx_user_product,unique" json:"user_id"`
|
||||||
|
ProductID string `gorm:"type:varchar(36);not null;index:idx_user_product,unique" json:"product_id"`
|
||||||
|
TotalQuota int64 `gorm:"type:bigint;not null;default:0" json:"total_quota"`
|
||||||
|
UsedQuota int64 `gorm:"type:bigint;not null;default:0" json:"used_quota"`
|
||||||
|
AvailableQuota int64 `gorm:"type:bigint;not null;default:0" json:"available_quota"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UserProductQuotaAccount) TableName() string {
|
||||||
|
return "user_product_quota_accounts"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *UserProductQuotaAccount) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
if a.ID == "" {
|
||||||
|
a.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserProductQuotaLedger 用户产品额度流水(通用模型,适配所有用户)
|
||||||
|
type UserProductQuotaLedger struct {
|
||||||
|
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
|
||||||
|
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id"`
|
||||||
|
ProductID string `gorm:"type:varchar(36);not null;index" json:"product_id"`
|
||||||
|
ChangeType string `gorm:"type:varchar(50);not null;index" json:"change_type"`
|
||||||
|
DeltaQuota int64 `gorm:"type:bigint;not null" json:"delta_quota"`
|
||||||
|
BeforeQuota int64 `gorm:"type:bigint;not null" json:"before_quota"`
|
||||||
|
AfterQuota int64 `gorm:"type:bigint;not null" json:"after_quota"`
|
||||||
|
SourceID string `gorm:"type:varchar(36);index" json:"source_id"`
|
||||||
|
OperatorID string `gorm:"type:varchar(36);not null" json:"operator_id"`
|
||||||
|
Remark string `gorm:"type:varchar(255)" json:"remark"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UserProductQuotaLedger) TableName() string {
|
||||||
|
return "user_product_quota_ledgers"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UserProductQuotaLedger) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
if l.ID == "" {
|
||||||
|
l.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -28,4 +28,15 @@ type SubordinateRepository interface {
|
|||||||
// 划拨
|
// 划拨
|
||||||
CreateWalletAllocation(ctx context.Context, a *entities.SubordinateWalletAllocation) error
|
CreateWalletAllocation(ctx context.Context, a *entities.SubordinateWalletAllocation) error
|
||||||
ListWalletAllocationsByParentAndChild(ctx context.Context, parentUserID, childUserID string, limit, offset int) ([]*entities.SubordinateWalletAllocation, int64, error)
|
ListWalletAllocationsByParentAndChild(ctx context.Context, parentUserID, childUserID string, limit, offset int) ([]*entities.SubordinateWalletAllocation, int64, error)
|
||||||
|
|
||||||
|
// 额度购买
|
||||||
|
CreateQuotaPurchase(ctx context.Context, p *entities.SubordinateQuotaPurchase) error
|
||||||
|
ListQuotaPurchasesByParentAndChild(ctx context.Context, parentUserID, childUserID string, limit, offset int) ([]*entities.SubordinateQuotaPurchase, int64, error)
|
||||||
|
|
||||||
|
// 额度账户
|
||||||
|
FindQuotaAccount(ctx context.Context, userID, productID string) (*entities.UserProductQuotaAccount, error)
|
||||||
|
CreateQuotaAccount(ctx context.Context, account *entities.UserProductQuotaAccount) error
|
||||||
|
UpdateQuotaAccount(ctx context.Context, account *entities.UserProductQuotaAccount) error
|
||||||
|
ListQuotaAccountsByUser(ctx context.Context, userID string) ([]*entities.UserProductQuotaAccount, error)
|
||||||
|
CreateQuotaLedger(ctx context.Context, ledger *entities.UserProductQuotaLedger) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,3 +199,67 @@ func (r *GormSubordinateRepository) ListWalletAllocationsByParentAndChild(ctx co
|
|||||||
}
|
}
|
||||||
return out, total, nil
|
return out, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateQuotaPurchase 创建额度购买记录
|
||||||
|
func (r *GormSubordinateRepository) CreateQuotaPurchase(ctx context.Context, p *entities.SubordinateQuotaPurchase) error {
|
||||||
|
return r.withCtx(ctx).Create(p).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListQuotaPurchasesByParentAndChild 查询主对子额度购买记录
|
||||||
|
func (r *GormSubordinateRepository) ListQuotaPurchasesByParentAndChild(ctx context.Context, parentUserID, childUserID string, limit, offset int) ([]*entities.SubordinateQuotaPurchase, int64, error) {
|
||||||
|
var list []entities.SubordinateQuotaPurchase
|
||||||
|
var total int64
|
||||||
|
q := r.withCtx(ctx).Model(&entities.SubordinateQuotaPurchase{}).Where("parent_user_id = ? AND child_user_id = ?", parentUserID, childUserID)
|
||||||
|
if err := q.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if err := q.Order("created_at DESC").Limit(limit).Offset(offset).Find(&list).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
out := make([]*entities.SubordinateQuotaPurchase, len(list))
|
||||||
|
for i := range list {
|
||||||
|
out[i] = &list[i]
|
||||||
|
}
|
||||||
|
return out, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindQuotaAccount 查询用户产品额度账户
|
||||||
|
func (r *GormSubordinateRepository) FindQuotaAccount(ctx context.Context, userID, productID string) (*entities.UserProductQuotaAccount, error) {
|
||||||
|
var account entities.UserProductQuotaAccount
|
||||||
|
err := r.withCtx(ctx).Where("user_id = ? AND product_id = ?", userID, productID).First(&account).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &account, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateQuotaAccount 创建额度账户
|
||||||
|
func (r *GormSubordinateRepository) CreateQuotaAccount(ctx context.Context, account *entities.UserProductQuotaAccount) error {
|
||||||
|
return r.withCtx(ctx).Create(account).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateQuotaAccount 更新额度账户
|
||||||
|
func (r *GormSubordinateRepository) UpdateQuotaAccount(ctx context.Context, account *entities.UserProductQuotaAccount) error {
|
||||||
|
return r.withCtx(ctx).Save(account).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListQuotaAccountsByUser 查询用户全部额度账户
|
||||||
|
func (r *GormSubordinateRepository) ListQuotaAccountsByUser(ctx context.Context, userID string) ([]*entities.UserProductQuotaAccount, error) {
|
||||||
|
var list []entities.UserProductQuotaAccount
|
||||||
|
if err := r.withCtx(ctx).Where("user_id = ?", userID).Order("updated_at DESC").Find(&list).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]*entities.UserProductQuotaAccount, len(list))
|
||||||
|
for i := range list {
|
||||||
|
out[i] = &list[i]
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateQuotaLedger 创建额度流水
|
||||||
|
func (r *GormSubordinateRepository) CreateQuotaLedger(ctx context.Context, ledger *entities.UserProductQuotaLedger) error {
|
||||||
|
return r.withCtx(ctx).Create(ledger).Error
|
||||||
|
}
|
||||||
|
|||||||
@@ -209,3 +209,91 @@ func (h *SubordinateHandler) RemoveChildSubscription(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
h.response.Success(c, nil, "删除成功")
|
h.response.Success(c, nil, "删除成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PurchaseQuota 为下属购买额度
|
||||||
|
func (h *SubordinateHandler) PurchaseQuota(c *gin.Context) {
|
||||||
|
parentID := c.GetString("user_id")
|
||||||
|
if parentID == "" {
|
||||||
|
h.response.Unauthorized(c, "未登录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var cmd commands.PurchaseChildQuotaCommand
|
||||||
|
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd.ParentUserID = parentID
|
||||||
|
if err := h.app.PurchaseChildQuota(c.Request.Context(), &cmd); err != nil {
|
||||||
|
h.logger.Error("为下属购买额度失败", zap.Error(err))
|
||||||
|
h.response.BadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.response.Success(c, nil, "购买额度成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListQuotaPurchases 下属额度购买记录
|
||||||
|
func (h *SubordinateHandler) ListQuotaPurchases(c *gin.Context) {
|
||||||
|
parentID := c.GetString("user_id")
|
||||||
|
if parentID == "" {
|
||||||
|
h.response.Unauthorized(c, "未登录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
size, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||||
|
cmd := &commands.ListChildQuotaPurchasesCommand{
|
||||||
|
ParentUserID: parentID,
|
||||||
|
ChildUserID: c.Query("child_user_id"),
|
||||||
|
Page: page,
|
||||||
|
PageSize: size,
|
||||||
|
}
|
||||||
|
if cmd.ChildUserID == "" {
|
||||||
|
h.response.BadRequest(c, "child_user_id 不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := h.app.ListChildQuotaPurchases(c.Request.Context(), cmd)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("获取额度购买记录失败", zap.Error(err))
|
||||||
|
h.response.BadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.response.Success(c, res, "获取成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListChildQuotaAccounts 下属额度账户
|
||||||
|
func (h *SubordinateHandler) ListChildQuotaAccounts(c *gin.Context) {
|
||||||
|
parentID := c.GetString("user_id")
|
||||||
|
if parentID == "" {
|
||||||
|
h.response.Unauthorized(c, "未登录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
childID := c.Query("child_user_id")
|
||||||
|
if childID == "" {
|
||||||
|
h.response.BadRequest(c, "child_user_id 不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := h.app.ListChildQuotaAccounts(c.Request.Context(), &commands.ListChildQuotaAccountsCommand{
|
||||||
|
ParentUserID: parentID,
|
||||||
|
ChildUserID: childID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("获取下属额度账户失败", zap.Error(err))
|
||||||
|
h.response.BadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.response.Success(c, res, "获取成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyQuotaAccounts 当前登录用户额度账户
|
||||||
|
func (h *SubordinateHandler) ListMyQuotaAccounts(c *gin.Context) {
|
||||||
|
userID := c.GetString("user_id")
|
||||||
|
if userID == "" {
|
||||||
|
h.response.Unauthorized(c, "未登录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := h.app.ListMyQuotaAccounts(c.Request.Context(), userID)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("获取我的额度账户失败", zap.Error(err))
|
||||||
|
h.response.BadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.response.Success(c, res, "获取成功")
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ func (r *SubordinateRoutes) Register(router *sharedhttp.GinRouter) {
|
|||||||
sub.POST("/assign-subscription", r.handler.AssignSubscription)
|
sub.POST("/assign-subscription", r.handler.AssignSubscription)
|
||||||
sub.GET("/child-subscriptions", r.handler.ListChildSubscriptions)
|
sub.GET("/child-subscriptions", r.handler.ListChildSubscriptions)
|
||||||
sub.DELETE("/child-subscriptions/:subscription_id", r.handler.RemoveChildSubscription)
|
sub.DELETE("/child-subscriptions/:subscription_id", r.handler.RemoveChildSubscription)
|
||||||
|
sub.POST("/purchase-quota", r.handler.PurchaseQuota)
|
||||||
|
sub.GET("/quota-purchases", r.handler.ListQuotaPurchases)
|
||||||
|
sub.GET("/child-quotas", r.handler.ListChildQuotaAccounts)
|
||||||
|
sub.GET("/my-quotas", r.handler.ListMyQuotaAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.Info("下属账号路由注册完成")
|
r.logger.Info("下属账号路由注册完成")
|
||||||
|
|||||||
Reference in New Issue
Block a user