From 59c09d6a33bda577b8ab1eb6d07f5f1b8277fcbc Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sat, 6 Jun 2026 14:45:22 +0800 Subject: [PATCH 1/2] f --- .../dto/commands/subordinate_commands.go | 30 ++++ .../dto/responses/subordinate_responses.go | 23 ++- .../subordinate_application_service.go | 5 +- .../subordinate_application_service_impl.go | 164 ++++++++++++++++-- internal/domains/subordinate/entities/link.go | 1 + .../repositories/subordinate_list_filter.go | 8 + .../subordinate_repository_interface.go | 3 +- .../gorm_subordinate_repository.go | 41 ++++- .../http/handlers/subordinate_handler.go | 62 ++++++- .../http/routes/subordinate_routes.go | 2 + 10 files changed, 315 insertions(+), 24 deletions(-) create mode 100644 internal/domains/subordinate/repositories/subordinate_list_filter.go diff --git a/internal/application/subordinate/dto/commands/subordinate_commands.go b/internal/application/subordinate/dto/commands/subordinate_commands.go index 02d7d4e..81b1be0 100644 --- a/internal/application/subordinate/dto/commands/subordinate_commands.go +++ b/internal/application/subordinate/dto/commands/subordinate_commands.go @@ -9,6 +9,36 @@ type SubPortalRegisterCommand struct { InviteToken string `json:"invite_token" binding:"required"` } +// ListChildApiCallsCommand 下属 API 调用记录查询 +type ListChildApiCallsCommand struct { + ParentUserID string + ChildUserID string `form:"child_user_id" binding:"required"` + Page int `form:"page"` + PageSize int `form:"page_size"` + TransactionID string `form:"transaction_id"` + ProductName string `form:"product_name"` + Status string `form:"status"` + StartTime string `form:"start_time"` + EndTime string `form:"end_time"` +} + +// ListSubordinatesCommand 下属列表查询 +type ListSubordinatesCommand struct { + ParentUserID string + Page int `form:"page"` + PageSize int `form:"page_size"` + Remark string `form:"remark"` + Phone string `form:"phone"` + CompanyName string `form:"company_name"` +} + +// UpdateSubordinateRemarkCommand 更新下属备注 +type UpdateSubordinateRemarkCommand struct { + ParentUserID string + ChildUserID string `json:"child_user_id" binding:"required"` + Remark string `json:"remark"` +} + // CreateInvitationCommand 主账号创建邀请 type CreateInvitationCommand struct { ParentUserID string diff --git a/internal/application/subordinate/dto/responses/subordinate_responses.go b/internal/application/subordinate/dto/responses/subordinate_responses.go index c019b13..8593e44 100644 --- a/internal/application/subordinate/dto/responses/subordinate_responses.go +++ b/internal/application/subordinate/dto/responses/subordinate_responses.go @@ -10,15 +10,24 @@ type CreateInvitationResponse struct { InvitationID string `json:"invitation_id"` } +// SubordinateProductQuotaItem 下属产品额度 +type SubordinateProductQuotaItem struct { + ProductID string `json:"product_id"` + ProductName string `json:"product_name"` + AvailableQuota int64 `json:"available_quota"` +} + // SubordinateListItem 下属一条 type SubordinateListItem struct { - ChildUserID string `json:"child_user_id"` - Phone string `json:"phone,omitempty"` - LinkID string `json:"link_id"` - RegisteredAt time.Time `json:"registered_at"` - CompanyName string `json:"company_name"` - IsCertified bool `json:"is_certified"` - Balance string `json:"balance"` + ChildUserID string `json:"child_user_id"` + Phone string `json:"phone,omitempty"` + LinkID string `json:"link_id"` + Remark string `json:"remark"` + RegisteredAt time.Time `json:"registered_at"` + CompanyName string `json:"company_name"` + IsCertified bool `json:"is_certified"` + Balance string `json:"balance"` + ProductQuotas []SubordinateProductQuotaItem `json:"product_quotas"` } // SubordinateListResponse 列表 diff --git a/internal/application/subordinate/subordinate_application_service.go b/internal/application/subordinate/subordinate_application_service.go index 39adb5b..ecbb282 100644 --- a/internal/application/subordinate/subordinate_application_service.go +++ b/internal/application/subordinate/subordinate_application_service.go @@ -3,6 +3,7 @@ package subordinate import ( "context" + api_dto "tyapi-server/internal/application/api/dto" "tyapi-server/internal/application/subordinate/dto/commands" "tyapi-server/internal/application/subordinate/dto/responses" ) @@ -11,7 +12,8 @@ import ( type SubordinateApplicationService interface { RegisterSubPortal(ctx context.Context, cmd *commands.SubPortalRegisterCommand) (*responses.SubPortalRegisterResponse, error) CreateInvitation(ctx context.Context, cmd *commands.CreateInvitationCommand) (*responses.CreateInvitationResponse, error) - ListMySubordinates(ctx context.Context, parentUserID string, page, pageSize int) (*responses.SubordinateListResponse, error) + ListMySubordinates(ctx context.Context, cmd *commands.ListSubordinatesCommand) (*responses.SubordinateListResponse, error) + UpdateSubordinateRemark(ctx context.Context, cmd *commands.UpdateSubordinateRemarkCommand) error AllocateToChild(ctx context.Context, cmd *commands.AllocateToChildCommand) error ListChildAllocations(ctx context.Context, cmd *commands.ListChildAllocationsCommand) (*responses.ChildAllocationListResponse, error) AssignChildSubscription(ctx context.Context, cmd *commands.AssignChildSubscriptionCommand) error @@ -20,5 +22,6 @@ type SubordinateApplicationService interface { 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) + ListChildApiCalls(ctx context.Context, cmd *commands.ListChildApiCallsCommand) (*api_dto.ApiCallListResponse, error) ListMyQuotaAccounts(ctx context.Context, userID string) ([]responses.ChildQuotaAccountItem, error) } diff --git a/internal/application/subordinate/subordinate_application_service_impl.go b/internal/application/subordinate/subordinate_application_service_impl.go index 16659f7..6db8e3f 100644 --- a/internal/application/subordinate/subordinate_application_service_impl.go +++ b/internal/application/subordinate/subordinate_application_service_impl.go @@ -11,18 +11,22 @@ import ( "github.com/shopspring/decimal" "go.uber.org/zap" + api_app "tyapi-server/internal/application/api" + api_dto "tyapi-server/internal/application/api/dto" "tyapi-server/internal/application/subordinate/dto/commands" "tyapi-server/internal/application/subordinate/dto/responses" "tyapi-server/internal/config" "tyapi-server/internal/domains/finance/repositories" productentities "tyapi-server/internal/domains/product/entities" product_service "tyapi-server/internal/domains/product/services" + product_repositories "tyapi-server/internal/domains/product/repositories" subentities "tyapi-server/internal/domains/subordinate/entities" subrepositories "tyapi-server/internal/domains/subordinate/repositories" user_entities "tyapi-server/internal/domains/user/entities" user_repositories "tyapi-server/internal/domains/user/repositories" domain_user_services "tyapi-server/internal/domains/user/services" "tyapi-server/internal/shared/database" + shared_interfaces "tyapi-server/internal/shared/interfaces" ) // SubordinateApplicationServiceImpl 实现 @@ -30,11 +34,13 @@ type SubordinateApplicationServiceImpl struct { subRepo subrepositories.SubordinateRepository userAgg domain_user_services.UserAggregateService smsService *domain_user_services.SMSCodeService - productSub *product_service.ProductSubscriptionService + productSub *product_service.ProductSubscriptionService + productRepo product_repositories.ProductRepository cfg *config.Config txm *database.TransactionManager walletRepo repositories.WalletRepository userRepo user_repositories.UserRepository + apiApp api_app.ApiApplicationService logger *zap.Logger } @@ -44,10 +50,12 @@ func NewSubordinateApplicationService( userAgg domain_user_services.UserAggregateService, smsService *domain_user_services.SMSCodeService, productSub *product_service.ProductSubscriptionService, + productRepo product_repositories.ProductRepository, cfg *config.Config, txm *database.TransactionManager, walletRepo repositories.WalletRepository, userRepo user_repositories.UserRepository, + apiApp api_app.ApiApplicationService, logger *zap.Logger, ) SubordinateApplicationService { return &SubordinateApplicationServiceImpl{ @@ -55,10 +63,12 @@ func NewSubordinateApplicationService( userAgg: userAgg, smsService: smsService, productSub: productSub, + productRepo: productRepo, cfg: cfg, txm: txm, walletRepo: walletRepo, userRepo: userRepo, + apiApp: apiApp, logger: logger, } } @@ -176,7 +186,9 @@ func (s *SubordinateApplicationServiceImpl) buildInvitationResponse(inv *subenti } // ListMySubordinates 主账号的下属 -func (s *SubordinateApplicationServiceImpl) ListMySubordinates(ctx context.Context, parentUserID string, page, pageSize int) (*responses.SubordinateListResponse, error) { +func (s *SubordinateApplicationServiceImpl) ListMySubordinates(ctx context.Context, cmd *commands.ListSubordinatesCommand) (*responses.SubordinateListResponse, error) { + page := cmd.Page + pageSize := cmd.PageSize if page < 1 { page = 1 } @@ -184,10 +196,58 @@ func (s *SubordinateApplicationServiceImpl) ListMySubordinates(ctx context.Conte pageSize = 20 } offset := (page - 1) * pageSize - links, total, err := s.subRepo.ListChildrenByParent(ctx, parentUserID, pageSize, offset) + filter := subrepositories.SubordinateListFilter{ + Remark: strings.TrimSpace(cmd.Remark), + Phone: strings.TrimSpace(cmd.Phone), + CompanyName: strings.TrimSpace(cmd.CompanyName), + } + links, total, err := s.subRepo.ListChildrenByParent(ctx, cmd.ParentUserID, filter, pageSize, offset) if err != nil { return nil, err } + + childIDs := make([]string, 0, len(links)) + for _, ln := range links { + childIDs = append(childIDs, ln.ChildUserID) + } + + quotaByUser := make(map[string][]*subentities.UserProductQuotaAccount) + if len(childIDs) > 0 { + accounts, quotaErr := s.subRepo.ListQuotaAccountsByUserIDs(ctx, childIDs) + if quotaErr != nil { + return nil, quotaErr + } + for _, account := range accounts { + quotaByUser[account.UserID] = append(quotaByUser[account.UserID], account) + } + } + + productNameMap := make(map[string]string) + if s.productRepo != nil { + productIDSet := make(map[string]struct{}) + for _, accounts := range quotaByUser { + for _, account := range accounts { + productIDSet[account.ProductID] = struct{}{} + } + } + if len(productIDSet) > 0 { + productIDs := make([]string, 0, len(productIDSet)) + for id := range productIDSet { + productIDs = append(productIDs, id) + } + products, productErr := s.productRepo.FindProductsByIDs(ctx, productIDs) + if productErr != nil { + s.logger.Warn("批量获取产品名称失败", zap.Error(productErr)) + } else { + for _, product := range products { + if product != nil { + productNameMap[product.ID] = product.Name + } + } + } + } + } + items := make([]responses.SubordinateListItem, 0, len(links)) for _, ln := range links { phone := "" @@ -209,19 +269,52 @@ func (s *SubordinateApplicationServiceImpl) ListMySubordinates(ctx context.Conte if w, e := s.walletRepo.GetByUserID(ctx, ln.ChildUserID); e == nil && w != nil { balance = w.Balance.StringFixed(2) } + + productQuotas := make([]responses.SubordinateProductQuotaItem, 0) + for _, account := range quotaByUser[ln.ChildUserID] { + name := productNameMap[account.ProductID] + if name == "" { + name = account.ProductID + } + productQuotas = append(productQuotas, responses.SubordinateProductQuotaItem{ + ProductID: account.ProductID, + ProductName: name, + AvailableQuota: account.AvailableQuota, + }) + } + items = append(items, responses.SubordinateListItem{ - ChildUserID: ln.ChildUserID, - Phone: phone, - LinkID: ln.ID, - RegisteredAt: registeredAt, - CompanyName: companyName, - IsCertified: isCertified, - Balance: balance, + ChildUserID: ln.ChildUserID, + Phone: phone, + LinkID: ln.ID, + Remark: ln.Remark, + RegisteredAt: registeredAt, + CompanyName: companyName, + IsCertified: isCertified, + Balance: balance, + ProductQuotas: productQuotas, }) } return &responses.SubordinateListResponse{Total: total, Items: items}, nil } +// UpdateSubordinateRemark 更新下属备注 +func (s *SubordinateApplicationServiceImpl) UpdateSubordinateRemark(ctx context.Context, cmd *commands.UpdateSubordinateRemarkCommand) error { + 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("该用户不是您的有效下属") + } + remark := strings.TrimSpace(cmd.Remark) + if len([]rune(remark)) > 255 { + return fmt.Errorf("备注不能超过255个字符") + } + lnk.Remark = remark + return s.subRepo.UpdateLink(ctx, lnk) +} + // AllocateToChild 划款 func (s *SubordinateApplicationServiceImpl) AllocateToChild(ctx context.Context, cmd *commands.AllocateToChildCommand) error { amount, err := decimal.NewFromString(strings.TrimSpace(cmd.Amount)) @@ -604,6 +697,57 @@ func (s *SubordinateApplicationServiceImpl) ListChildQuotaAccounts(ctx context.C return items, nil } +// ListChildApiCalls 主账号查看下属 API 调用记录 +func (s *SubordinateApplicationServiceImpl) ListChildApiCalls(ctx context.Context, cmd *commands.ListChildApiCallsCommand) (*api_dto.ApiCallListResponse, error) { + if s.apiApp == nil { + return nil, fmt.Errorf("API 服务未初始化") + } + 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 = 10 + } + + filters := make(map[string]interface{}) + if strings.TrimSpace(cmd.TransactionID) != "" { + filters["transaction_id"] = strings.TrimSpace(cmd.TransactionID) + } + if strings.TrimSpace(cmd.ProductName) != "" { + filters["product_name"] = strings.TrimSpace(cmd.ProductName) + } + if strings.TrimSpace(cmd.Status) != "" { + filters["status"] = strings.TrimSpace(cmd.Status) + } + if strings.TrimSpace(cmd.StartTime) != "" { + if t, parseErr := time.Parse("2006-01-02 15:04:05", strings.TrimSpace(cmd.StartTime)); parseErr == nil { + filters["start_time"] = t + } + } + if strings.TrimSpace(cmd.EndTime) != "" { + if t, parseErr := time.Parse("2006-01-02 15:04:05", strings.TrimSpace(cmd.EndTime)); parseErr == nil { + filters["end_time"] = t + } + } + + return s.apiApp.GetUserApiCalls(ctx, cmd.ChildUserID, filters, shared_interfaces.ListOptions{ + Page: page, + PageSize: pageSize, + Sort: "created_at", + Order: "desc", + }) +} + // ListMyQuotaAccounts 查询当前用户额度账户(通用能力,适配所有用户) func (s *SubordinateApplicationServiceImpl) ListMyQuotaAccounts(ctx context.Context, userID string) ([]responses.ChildQuotaAccountItem, error) { accounts, err := s.subRepo.ListQuotaAccountsByUser(ctx, userID) diff --git a/internal/domains/subordinate/entities/link.go b/internal/domains/subordinate/entities/link.go index 9b77540..10e7bfd 100644 --- a/internal/domains/subordinate/entities/link.go +++ b/internal/domains/subordinate/entities/link.go @@ -21,6 +21,7 @@ type UserSubordinateLink struct { ParentUserID string `gorm:"type:varchar(36);not null;index:idx_parent,priority:1" json:"parent_user_id" comment:"主账号用户ID"` ChildUserID string `gorm:"type:varchar(36);not null;uniqueIndex" json:"child_user_id" comment:"子账号用户ID(唯一)"` InvitationID *string `gorm:"type:varchar(36);index" json:"invitation_id,omitempty" comment:"关联的邀请ID"` + Remark string `gorm:"type:varchar(255)" json:"remark" comment:"主账号备注"` Status LinkStatus `gorm:"type:varchar(20);not null;default:active" json:"status" comment:"状态"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` diff --git a/internal/domains/subordinate/repositories/subordinate_list_filter.go b/internal/domains/subordinate/repositories/subordinate_list_filter.go new file mode 100644 index 0000000..e4dfd7b --- /dev/null +++ b/internal/domains/subordinate/repositories/subordinate_list_filter.go @@ -0,0 +1,8 @@ +package repositories + +// SubordinateListFilter 下属列表筛选 +type SubordinateListFilter struct { + Remark string + Phone string + CompanyName string +} diff --git a/internal/domains/subordinate/repositories/subordinate_repository_interface.go b/internal/domains/subordinate/repositories/subordinate_repository_interface.go index 04982a7..8c051db 100644 --- a/internal/domains/subordinate/repositories/subordinate_repository_interface.go +++ b/internal/domains/subordinate/repositories/subordinate_repository_interface.go @@ -21,7 +21,8 @@ type SubordinateRepository interface { CreateLink(ctx context.Context, link *entities.UserSubordinateLink) error FindLinkByChildUserID(ctx context.Context, childUserID string) (*entities.UserSubordinateLink, error) FindLinkByParentAndChild(ctx context.Context, parentUserID, childUserID string) (*entities.UserSubordinateLink, error) - ListChildrenByParent(ctx context.Context, parentUserID string, limit, offset int) ([]*entities.UserSubordinateLink, int64, error) + ListChildrenByParent(ctx context.Context, parentUserID string, filter SubordinateListFilter, limit, offset int) ([]*entities.UserSubordinateLink, int64, error) + ListQuotaAccountsByUserIDs(ctx context.Context, userIDs []string) ([]*entities.UserProductQuotaAccount, error) UpdateLink(ctx context.Context, link *entities.UserSubordinateLink) error // 是否存在子账号关系(任意子账号) IsUserSubordinate(ctx context.Context, userID string) (bool, error) diff --git a/internal/infrastructure/database/repositories/subordinate/gorm_subordinate_repository.go b/internal/infrastructure/database/repositories/subordinate/gorm_subordinate_repository.go index 655d33e..9e99c7c 100644 --- a/internal/infrastructure/database/repositories/subordinate/gorm_subordinate_repository.go +++ b/internal/infrastructure/database/repositories/subordinate/gorm_subordinate_repository.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strings" "time" "go.uber.org/zap" @@ -152,15 +153,31 @@ func (r *GormSubordinateRepository) FindLinkByParentAndChild(ctx context.Context return &l, nil } -// ListChildrenByParent 列出下属 -func (r *GormSubordinateRepository) ListChildrenByParent(ctx context.Context, parentUserID string, limit, offset int) ([]*entities.UserSubordinateLink, int64, error) { +// ListChildrenByParent 列出下属(支持备注/手机号/公司名模糊筛选) +func (r *GormSubordinateRepository) ListChildrenByParent(ctx context.Context, parentUserID string, filter repositories.SubordinateListFilter, limit, offset int) ([]*entities.UserSubordinateLink, int64, error) { var list []entities.UserSubordinateLink var total int64 - q := r.withCtx(ctx).Model(&entities.UserSubordinateLink{}).Where("parent_user_id = ? AND status = ?", parentUserID, entities.LinkStatusActive) + q := r.withCtx(ctx).Model(&entities.UserSubordinateLink{}). + Where("user_subordinate_links.parent_user_id = ? AND user_subordinate_links.status = ?", parentUserID, entities.LinkStatusActive) + + if strings.TrimSpace(filter.Remark) != "" { + q = q.Where("user_subordinate_links.remark LIKE ?", "%"+strings.TrimSpace(filter.Remark)+"%") + } + if strings.TrimSpace(filter.Phone) != "" || strings.TrimSpace(filter.CompanyName) != "" { + q = q.Joins("JOIN users ON users.id = user_subordinate_links.child_user_id AND users.deleted_at IS NULL") + if strings.TrimSpace(filter.Phone) != "" { + q = q.Where("users.phone LIKE ?", "%"+strings.TrimSpace(filter.Phone)+"%") + } + if strings.TrimSpace(filter.CompanyName) != "" { + q = q.Joins("LEFT JOIN enterprise_infos ON enterprise_infos.user_id = users.id AND enterprise_infos.deleted_at IS NULL"). + Where("enterprise_infos.company_name LIKE ?", "%"+strings.TrimSpace(filter.CompanyName)+"%") + } + } + 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 { + if err := q.Order("user_subordinate_links.created_at DESC").Limit(limit).Offset(offset).Find(&list).Error; err != nil { return nil, 0, err } out := make([]*entities.UserSubordinateLink, len(list)) @@ -185,6 +202,22 @@ func (r *GormSubordinateRepository) IsUserSubordinate(ctx context.Context, userI return n > 0, nil } +// ListQuotaAccountsByUserIDs 批量查询用户产品额度账户 +func (r *GormSubordinateRepository) ListQuotaAccountsByUserIDs(ctx context.Context, userIDs []string) ([]*entities.UserProductQuotaAccount, error) { + if len(userIDs) == 0 { + return nil, nil + } + var list []entities.UserProductQuotaAccount + if err := r.withCtx(ctx).Where("user_id IN ?", userIDs).Order("user_id ASC, 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 +} + // CreateWalletAllocation 记划拨 func (r *GormSubordinateRepository) CreateWalletAllocation(ctx context.Context, a *entities.SubordinateWalletAllocation) error { // 幂等:同 business_ref 不重复 diff --git a/internal/infrastructure/http/handlers/subordinate_handler.go b/internal/infrastructure/http/handlers/subordinate_handler.go index db24da4..7321bea 100644 --- a/internal/infrastructure/http/handlers/subordinate_handler.go +++ b/internal/infrastructure/http/handlers/subordinate_handler.go @@ -76,7 +76,14 @@ func (h *SubordinateHandler) ListSubordinates(c *gin.Context) { } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) - res, err := h.app.ListMySubordinates(c.Request.Context(), parentID, page, size) + res, err := h.app.ListMySubordinates(c.Request.Context(), &commands.ListSubordinatesCommand{ + ParentUserID: parentID, + Page: page, + PageSize: size, + Remark: c.Query("remark"), + Phone: c.Query("phone"), + CompanyName: c.Query("company_name"), + }) if err != nil { h.logger.Error("获取下属列表失败", zap.Error(err)) h.response.InternalError(c, "获取下属列表失败") @@ -85,6 +92,26 @@ func (h *SubordinateHandler) ListSubordinates(c *gin.Context) { h.response.Success(c, res, "获取成功") } +// UpdateSubordinateRemark 更新下属备注 +func (h *SubordinateHandler) UpdateSubordinateRemark(c *gin.Context) { + parentID := c.GetString("user_id") + if parentID == "" { + h.response.Unauthorized(c, "未登录") + return + } + var cmd commands.UpdateSubordinateRemarkCommand + if err := h.validator.BindAndValidate(c, &cmd); err != nil { + return + } + cmd.ParentUserID = parentID + if err := h.app.UpdateSubordinateRemark(c.Request.Context(), &cmd); err != nil { + h.logger.Error("更新下属备注失败", zap.Error(err)) + h.response.BadRequest(c, err.Error()) + return + } + h.response.Success(c, nil, "备注已更新") +} + // Allocate 划款 func (h *SubordinateHandler) Allocate(c *gin.Context) { parentID := c.GetString("user_id") @@ -282,6 +309,39 @@ func (h *SubordinateHandler) ListChildQuotaAccounts(c *gin.Context) { h.response.Success(c, res, "获取成功") } +// ListChildApiCalls 下属 API 调用记录 +func (h *SubordinateHandler) ListChildApiCalls(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 + } + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + size, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) + res, err := h.app.ListChildApiCalls(c.Request.Context(), &commands.ListChildApiCallsCommand{ + ParentUserID: parentID, + ChildUserID: childID, + Page: page, + PageSize: size, + TransactionID: c.Query("transaction_id"), + ProductName: c.Query("product_name"), + Status: c.Query("status"), + StartTime: c.Query("start_time"), + EndTime: c.Query("end_time"), + }) + 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") diff --git a/internal/infrastructure/http/routes/subordinate_routes.go b/internal/infrastructure/http/routes/subordinate_routes.go index 0bb7991..7abd00e 100644 --- a/internal/infrastructure/http/routes/subordinate_routes.go +++ b/internal/infrastructure/http/routes/subordinate_routes.go @@ -35,6 +35,7 @@ func (r *SubordinateRoutes) Register(router *sharedhttp.GinRouter) { { sub.POST("/invitations", r.handler.CreateInvitation) sub.GET("/subordinates", r.handler.ListSubordinates) + sub.PATCH("/subordinates/remark", r.handler.UpdateSubordinateRemark) sub.POST("/allocate", r.handler.Allocate) sub.GET("/allocations", r.handler.ListAllocations) sub.POST("/assign-subscription", r.handler.AssignSubscription) @@ -43,6 +44,7 @@ func (r *SubordinateRoutes) Register(router *sharedhttp.GinRouter) { sub.POST("/purchase-quota", r.handler.PurchaseQuota) sub.GET("/quota-purchases", r.handler.ListQuotaPurchases) sub.GET("/child-quotas", r.handler.ListChildQuotaAccounts) + sub.GET("/child-api-calls", r.handler.ListChildApiCalls) sub.GET("/my-quotas", r.handler.ListMyQuotaAccounts) } From 32f9299576dde2999d796eb609e01a70c66e8566 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sun, 7 Jun 2026 12:52:10 +0800 Subject: [PATCH 2/2] f --- resources/qiye.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/resources/qiye.html b/resources/qiye.html index dd23eb0..d0db211 100644 --- a/resources/qiye.html +++ b/resources/qiye.html @@ -3,7 +3,7 @@
-