f
This commit is contained in:
@@ -269,6 +269,9 @@ func (a *Application) autoMigrate(db *gorm.DB) error {
|
||||
&subordinateEntities.SubordinateInvitation{},
|
||||
&subordinateEntities.UserSubordinateLink{},
|
||||
&subordinateEntities.SubordinateWalletAllocation{},
|
||||
&subordinateEntities.SubordinateQuotaPurchase{},
|
||||
&subordinateEntities.UserProductQuotaAccount{},
|
||||
&subordinateEntities.UserProductQuotaLedger{},
|
||||
|
||||
// 任务域
|
||||
&taskEntities.AsyncTask{},
|
||||
|
||||
@@ -53,3 +53,26 @@ type RemoveChildSubscriptionCommand struct {
|
||||
ChildUserID string `json:"child_user_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"`
|
||||
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
|
||||
ListChildSubscriptions(ctx context.Context, cmd *commands.ListChildSubscriptionsCommand) ([]responses.ChildSubscriptionItem, 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
||||
// 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, "删除成功")
|
||||
}
|
||||
|
||||
// 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.GET("/child-subscriptions", r.handler.ListChildSubscriptions)
|
||||
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("下属账号路由注册完成")
|
||||
|
||||
Reference in New Issue
Block a user