This commit is contained in:
2026-06-06 14:45:22 +08:00
parent e4eb41ce10
commit 59c09d6a33
10 changed files with 315 additions and 24 deletions

View File

@@ -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 不重复

View File

@@ -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")

View File

@@ -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)
}