feat: complete superadmin management endpoints
This commit is contained in:
@@ -88,8 +88,118 @@ func (s *super) CheckToken(ctx context.Context, token string) (*super_dto.LoginR
|
||||
|
||||
func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.UserQuery.QueryContext(ctx)
|
||||
if filter.Username != nil && *filter.Username != "" {
|
||||
q = q.Where(tbl.Username.Like("%" + *filter.Username + "%")).Or(tbl.Nickname.Like("%" + *filter.Username + "%"))
|
||||
if filter.ID != nil && *filter.ID > 0 {
|
||||
q = q.Where(tbl.ID.Eq(*filter.ID))
|
||||
}
|
||||
if filter.Username != nil && strings.TrimSpace(*filter.Username) != "" {
|
||||
keyword := "%" + strings.TrimSpace(*filter.Username) + "%"
|
||||
q = q.Where(tbl.Username.Like(keyword)).Or(tbl.Nickname.Like(keyword))
|
||||
}
|
||||
if filter.Status != nil && *filter.Status != "" {
|
||||
q = q.Where(tbl.Status.Eq(*filter.Status))
|
||||
}
|
||||
if filter.Role != nil && *filter.Role != "" {
|
||||
q = q.Where(tbl.Roles.Contains(types.Array[consts.Role]{*filter.Role}))
|
||||
}
|
||||
if filter.TenantID != nil && *filter.TenantID > 0 {
|
||||
// 按租户成员过滤用户,需要先定位租户成员关系。
|
||||
tblTu, qTu := models.TenantUserQuery.QueryContext(ctx)
|
||||
userIDs, err := qTu.Where(tblTu.TenantID.Eq(*filter.TenantID)).Select(tblTu.UserID).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
ids := make([]int64, 0, len(userIDs))
|
||||
for _, row := range userIDs {
|
||||
ids = append(ids, row.UserID)
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
q = q.Where(tbl.ID.Eq(-1))
|
||||
} else {
|
||||
q = q.Where(tbl.ID.In(ids...))
|
||||
}
|
||||
}
|
||||
|
||||
if filter.CreatedAtFrom != nil {
|
||||
from, err := s.parseFilterTime(filter.CreatedAtFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from != nil {
|
||||
q = q.Where(tbl.CreatedAt.Gte(*from))
|
||||
}
|
||||
}
|
||||
if filter.CreatedAtTo != nil {
|
||||
to, err := s.parseFilterTime(filter.CreatedAtTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if to != nil {
|
||||
q = q.Where(tbl.CreatedAt.Lte(*to))
|
||||
}
|
||||
}
|
||||
if filter.VerifiedAtFrom != nil {
|
||||
from, err := s.parseFilterTime(filter.VerifiedAtFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from != nil {
|
||||
q = q.Where(tbl.VerifiedAt.Gte(*from))
|
||||
}
|
||||
}
|
||||
if filter.VerifiedAtTo != nil {
|
||||
to, err := s.parseFilterTime(filter.VerifiedAtTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if to != nil {
|
||||
q = q.Where(tbl.VerifiedAt.Lte(*to))
|
||||
}
|
||||
}
|
||||
|
||||
orderApplied := false
|
||||
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
|
||||
switch strings.TrimSpace(*filter.Desc) {
|
||||
case "id":
|
||||
q = q.Order(tbl.ID.Desc())
|
||||
case "username":
|
||||
q = q.Order(tbl.Username.Desc())
|
||||
case "status":
|
||||
q = q.Order(tbl.Status.Desc())
|
||||
case "verified_at":
|
||||
q = q.Order(tbl.VerifiedAt.Desc())
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt.Desc())
|
||||
case "updated_at":
|
||||
q = q.Order(tbl.UpdatedAt.Desc())
|
||||
case "balance":
|
||||
q = q.Order(tbl.Balance.Desc())
|
||||
case "balance_frozen":
|
||||
q = q.Order(tbl.BalanceFrozen.Desc())
|
||||
}
|
||||
orderApplied = true
|
||||
} else if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
|
||||
switch strings.TrimSpace(*filter.Asc) {
|
||||
case "id":
|
||||
q = q.Order(tbl.ID)
|
||||
case "username":
|
||||
q = q.Order(tbl.Username)
|
||||
case "status":
|
||||
q = q.Order(tbl.Status)
|
||||
case "verified_at":
|
||||
q = q.Order(tbl.VerifiedAt)
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt)
|
||||
case "updated_at":
|
||||
q = q.Order(tbl.UpdatedAt)
|
||||
case "balance":
|
||||
q = q.Order(tbl.Balance)
|
||||
case "balance_frozen":
|
||||
q = q.Order(tbl.BalanceFrozen)
|
||||
}
|
||||
orderApplied = true
|
||||
}
|
||||
if !orderApplied {
|
||||
q = q.Order(tbl.ID.Desc())
|
||||
}
|
||||
|
||||
filter.Pagination.Format()
|
||||
@@ -103,6 +213,20 @@ func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter)
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
userIDs := make([]int64, 0, len(list))
|
||||
for _, u := range list {
|
||||
userIDs = append(userIDs, u.ID)
|
||||
}
|
||||
|
||||
ownedCountMap, err := s.userOwnedTenantCount(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
joinedCountMap, err := s.userJoinedTenantCount(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data []super_dto.UserItem
|
||||
for _, u := range list {
|
||||
data = append(data, super_dto.UserItem{
|
||||
@@ -112,11 +236,14 @@ func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter)
|
||||
Roles: u.Roles,
|
||||
Status: u.Status,
|
||||
StatusDescription: u.Status.Description(),
|
||||
CreatedAt: u.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: u.UpdatedAt.Format(time.RFC3339),
|
||||
VerifiedAt: s.formatTime(u.VerifiedAt),
|
||||
CreatedAt: s.formatTime(u.CreatedAt),
|
||||
UpdatedAt: s.formatTime(u.UpdatedAt),
|
||||
},
|
||||
Balance: u.Balance,
|
||||
BalanceFrozen: u.BalanceFrozen,
|
||||
Balance: u.Balance,
|
||||
BalanceFrozen: u.BalanceFrozen,
|
||||
OwnedTenantCount: ownedCountMap[u.ID],
|
||||
JoinedTenantCount: joinedCountMap[u.ID],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -175,8 +302,102 @@ func (s *super) UpdateUserRoles(ctx context.Context, id int64, form *super_dto.U
|
||||
|
||||
func (s *super) ListTenants(ctx context.Context, filter *super_dto.TenantListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||
if filter.Name != nil && *filter.Name != "" {
|
||||
q = q.Where(tbl.Name.Like("%" + *filter.Name + "%"))
|
||||
if filter.ID != nil && *filter.ID > 0 {
|
||||
q = q.Where(tbl.ID.Eq(*filter.ID))
|
||||
}
|
||||
if filter.UserID != nil && *filter.UserID > 0 {
|
||||
q = q.Where(tbl.UserID.Eq(*filter.UserID))
|
||||
}
|
||||
if filter.Name != nil && strings.TrimSpace(*filter.Name) != "" {
|
||||
q = q.Where(tbl.Name.Like("%" + strings.TrimSpace(*filter.Name) + "%"))
|
||||
}
|
||||
if filter.Code != nil && strings.TrimSpace(*filter.Code) != "" {
|
||||
q = q.Where(tbl.Code.Like("%" + strings.TrimSpace(*filter.Code) + "%"))
|
||||
}
|
||||
if filter.Status != nil && *filter.Status != "" {
|
||||
q = q.Where(tbl.Status.Eq(*filter.Status))
|
||||
}
|
||||
if filter.ExpiredAtFrom != nil {
|
||||
from, err := s.parseFilterTime(filter.ExpiredAtFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from != nil {
|
||||
q = q.Where(tbl.ExpiredAt.Gte(*from))
|
||||
}
|
||||
}
|
||||
if filter.ExpiredAtTo != nil {
|
||||
to, err := s.parseFilterTime(filter.ExpiredAtTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if to != nil {
|
||||
q = q.Where(tbl.ExpiredAt.Lte(*to))
|
||||
}
|
||||
}
|
||||
if filter.CreatedAtFrom != nil {
|
||||
from, err := s.parseFilterTime(filter.CreatedAtFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from != nil {
|
||||
q = q.Where(tbl.CreatedAt.Gte(*from))
|
||||
}
|
||||
}
|
||||
if filter.CreatedAtTo != nil {
|
||||
to, err := s.parseFilterTime(filter.CreatedAtTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if to != nil {
|
||||
q = q.Where(tbl.CreatedAt.Lte(*to))
|
||||
}
|
||||
}
|
||||
|
||||
orderApplied := false
|
||||
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
|
||||
switch strings.TrimSpace(*filter.Desc) {
|
||||
case "id":
|
||||
q = q.Order(tbl.ID.Desc())
|
||||
case "name":
|
||||
q = q.Order(tbl.Name.Desc())
|
||||
case "code":
|
||||
q = q.Order(tbl.Code.Desc())
|
||||
case "status":
|
||||
q = q.Order(tbl.Status.Desc())
|
||||
case "expired_at":
|
||||
q = q.Order(tbl.ExpiredAt.Desc())
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt.Desc())
|
||||
case "updated_at":
|
||||
q = q.Order(tbl.UpdatedAt.Desc())
|
||||
case "user_id":
|
||||
q = q.Order(tbl.UserID.Desc())
|
||||
}
|
||||
orderApplied = true
|
||||
} else if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
|
||||
switch strings.TrimSpace(*filter.Asc) {
|
||||
case "id":
|
||||
q = q.Order(tbl.ID)
|
||||
case "name":
|
||||
q = q.Order(tbl.Name)
|
||||
case "code":
|
||||
q = q.Order(tbl.Code)
|
||||
case "status":
|
||||
q = q.Order(tbl.Status)
|
||||
case "expired_at":
|
||||
q = q.Order(tbl.ExpiredAt)
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt)
|
||||
case "updated_at":
|
||||
q = q.Order(tbl.UpdatedAt)
|
||||
case "user_id":
|
||||
q = q.Order(tbl.UserID)
|
||||
}
|
||||
orderApplied = true
|
||||
}
|
||||
if !orderApplied {
|
||||
q = q.Order(tbl.ID.Desc())
|
||||
}
|
||||
|
||||
filter.Pagination.Format()
|
||||
@@ -185,24 +406,14 @@ func (s *super) ListTenants(ctx context.Context, filter *super_dto.TenantListFil
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find()
|
||||
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
var data []super_dto.TenantItem
|
||||
for _, t := range list {
|
||||
data = append(data, super_dto.TenantItem{
|
||||
ID: t.ID,
|
||||
UUID: t.UUID.String(),
|
||||
Name: t.Name,
|
||||
Code: t.Code,
|
||||
Status: t.Status,
|
||||
StatusDescription: t.Status.Description(),
|
||||
UserID: t.UserID,
|
||||
CreatedAt: t.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: t.UpdatedAt.Format(time.RFC3339),
|
||||
})
|
||||
data, err := s.buildTenantItems(ctx, list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
@@ -240,17 +451,14 @@ func (s *super) GetTenant(ctx context.Context, id int64) (*super_dto.TenantItem,
|
||||
}
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return &super_dto.TenantItem{
|
||||
ID: t.ID,
|
||||
UUID: t.UUID.String(),
|
||||
Name: t.Name,
|
||||
Code: t.Code,
|
||||
Status: t.Status,
|
||||
StatusDescription: t.Status.Description(),
|
||||
UserID: t.UserID,
|
||||
CreatedAt: t.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: t.UpdatedAt.Format(time.RFC3339),
|
||||
}, nil
|
||||
items, err := s.buildTenantItems(ctx, []*models.Tenant{t})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(items) == 0 {
|
||||
return nil, errorx.ErrRecordNotFound
|
||||
}
|
||||
return &items[0], nil
|
||||
}
|
||||
|
||||
func (s *super) UpdateTenantStatus(ctx context.Context, id int64, form *super_dto.TenantStatusUpdateForm) error {
|
||||
@@ -272,6 +480,177 @@ func (s *super) UpdateTenantExpire(ctx context.Context, id int64, form *super_dt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *super) ListTenantUsers(ctx context.Context, tenantID int64, filter *super_dto.SuperTenantUserListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.TenantUserQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.TenantID.Eq(tenantID))
|
||||
if filter.UserID != nil && *filter.UserID > 0 {
|
||||
q = q.Where(tbl.UserID.Eq(*filter.UserID))
|
||||
}
|
||||
if filter.Status != nil && *filter.Status != "" {
|
||||
q = q.Where(tbl.Status.Eq(*filter.Status))
|
||||
}
|
||||
if filter.Role != nil && *filter.Role != "" {
|
||||
q = q.Where(tbl.Role.Contains(types.Array[consts.TenantUserRole]{*filter.Role}))
|
||||
}
|
||||
|
||||
userIDs, userFilter, err := s.lookupUserIDs(ctx, filter.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userFilter {
|
||||
if len(userIDs) == 0 {
|
||||
q = q.Where(tbl.ID.Eq(-1))
|
||||
} else {
|
||||
q = q.Where(tbl.UserID.In(userIDs...))
|
||||
}
|
||||
}
|
||||
|
||||
filter.Pagination.Format()
|
||||
total, err := q.Count()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.CreatedAt.Desc()).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
userMap, err := s.userMapByTenantUsers(ctx, list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]super_dto.SuperTenantUserItem, 0, len(list))
|
||||
for _, tu := range list {
|
||||
items = append(items, super_dto.SuperTenantUserItem{
|
||||
User: s.toSuperUserLite(userMap[tu.UserID]),
|
||||
TenantUser: s.toSuperTenantUserDTO(tu),
|
||||
})
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
Pagination: filter.Pagination,
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) ListUserTenants(ctx context.Context, userID int64, filter *super_dto.SuperUserTenantListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.TenantUserQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.UserID.Eq(userID))
|
||||
|
||||
if filter.TenantID != nil && *filter.TenantID > 0 {
|
||||
q = q.Where(tbl.TenantID.Eq(*filter.TenantID))
|
||||
}
|
||||
if filter.Status != nil && *filter.Status != "" {
|
||||
q = q.Where(tbl.Status.Eq(*filter.Status))
|
||||
}
|
||||
if filter.Role != nil && *filter.Role != "" {
|
||||
q = q.Where(tbl.Role.Contains(types.Array[consts.TenantUserRole]{*filter.Role}))
|
||||
}
|
||||
|
||||
tenantIDs, tenantFilter, err := s.lookupTenantIDs(ctx, filter.Code, filter.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tenantFilter {
|
||||
if len(tenantIDs) == 0 {
|
||||
q = q.Where(tbl.ID.Eq(-1))
|
||||
} else {
|
||||
q = q.Where(tbl.TenantID.In(tenantIDs...))
|
||||
}
|
||||
}
|
||||
|
||||
if filter.CreatedAtFrom != nil {
|
||||
from, err := s.parseFilterTime(filter.CreatedAtFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from != nil {
|
||||
q = q.Where(tbl.CreatedAt.Gte(*from))
|
||||
}
|
||||
}
|
||||
if filter.CreatedAtTo != nil {
|
||||
to, err := s.parseFilterTime(filter.CreatedAtTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if to != nil {
|
||||
q = q.Where(tbl.CreatedAt.Lte(*to))
|
||||
}
|
||||
}
|
||||
|
||||
orderApplied := false
|
||||
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
|
||||
switch strings.TrimSpace(*filter.Desc) {
|
||||
case "tenant_id":
|
||||
q = q.Order(tbl.TenantID.Desc())
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt.Desc())
|
||||
}
|
||||
orderApplied = true
|
||||
} else if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
|
||||
switch strings.TrimSpace(*filter.Asc) {
|
||||
case "tenant_id":
|
||||
q = q.Order(tbl.TenantID)
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt)
|
||||
}
|
||||
orderApplied = true
|
||||
}
|
||||
if !orderApplied {
|
||||
q = q.Order(tbl.CreatedAt.Desc())
|
||||
}
|
||||
|
||||
filter.Pagination.Format()
|
||||
total, err := q.Count()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
tenantMap, ownerMap, err := s.tenantMapsForTenantUsers(ctx, list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]super_dto.UserTenantItem, 0, len(list))
|
||||
for _, tu := range list {
|
||||
tenant := tenantMap[tu.TenantID]
|
||||
owner := ownerMap[tu.TenantID]
|
||||
item := super_dto.UserTenantItem{
|
||||
TenantID: tu.TenantID,
|
||||
Role: tu.Role,
|
||||
MemberStatus: tu.Status,
|
||||
MemberStatusDescription: tu.Status.Description(),
|
||||
JoinedAt: s.formatTime(tu.CreatedAt),
|
||||
}
|
||||
if tenant != nil {
|
||||
item.TenantStatus = tenant.Status
|
||||
item.TenantStatusDescription = tenant.Status.Description()
|
||||
item.Name = tenant.Name
|
||||
item.Code = tenant.Code
|
||||
item.ExpiredAt = s.formatTime(tenant.ExpiredAt)
|
||||
}
|
||||
if owner != nil {
|
||||
item.Owner = &super_dto.TenantOwnerUserLite{
|
||||
ID: owner.ID,
|
||||
Username: owner.Username,
|
||||
}
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
Pagination: filter.Pagination,
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) ListContents(ctx context.Context, filter *super_dto.SuperContentListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
|
||||
@@ -804,6 +1183,22 @@ func (s *super) toSuperUserDTO(u *models.User) *super_dto.User {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *super) toSuperUserLite(u *models.User) *super_dto.SuperUserLite {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return &super_dto.SuperUserLite{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Roles: u.Roles,
|
||||
Status: u.Status,
|
||||
StatusDescription: u.Status.Description(),
|
||||
VerifiedAt: s.formatTime(u.VerifiedAt),
|
||||
CreatedAt: s.formatTime(u.CreatedAt),
|
||||
UpdatedAt: s.formatTime(u.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func hasRole(roles types.Array[consts.Role], role consts.Role) bool {
|
||||
for _, r := range roles {
|
||||
if r == role {
|
||||
@@ -813,6 +1208,15 @@ func hasRole(roles types.Array[consts.Role], role consts.Role) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func hasTenantRole(roles types.Array[consts.TenantUserRole], role consts.TenantUserRole) bool {
|
||||
for _, r := range roles {
|
||||
if r == role {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *super) buildSuperOrderItems(ctx context.Context, orders []*models.Order) ([]super_dto.SuperOrderItem, error) {
|
||||
if len(orders) == 0 {
|
||||
return []super_dto.SuperOrderItem{}, nil
|
||||
@@ -1212,6 +1616,139 @@ func (s *super) toSuperDiscountValue(price *models.ContentPrice) float64 {
|
||||
return float64(price.DiscountValue)
|
||||
}
|
||||
|
||||
func (s *super) buildTenantItems(ctx context.Context, list []*models.Tenant) ([]super_dto.TenantItem, error) {
|
||||
if len(list) == 0 {
|
||||
return []super_dto.TenantItem{}, nil
|
||||
}
|
||||
|
||||
tenantIDs := make([]int64, 0, len(list))
|
||||
tenantOwnerIDs := make(map[int64]struct{}, len(list))
|
||||
for _, t := range list {
|
||||
tenantIDs = append(tenantIDs, t.ID)
|
||||
tenantOwnerIDs[t.UserID] = struct{}{}
|
||||
}
|
||||
|
||||
// 统计租户成员数与管理员列表(基于 tenant_users)。
|
||||
userCountMap := make(map[int64]int64, len(list))
|
||||
adminSet := make(map[int64]map[int64]struct{}, len(list))
|
||||
tblTu, qTu := models.TenantUserQuery.QueryContext(ctx)
|
||||
tenantUsers, err := qTu.Where(tblTu.TenantID.In(tenantIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, tu := range tenantUsers {
|
||||
userCountMap[tu.TenantID]++
|
||||
if hasTenantRole(tu.Role, consts.TenantUserRoleTenantAdmin) {
|
||||
if _, ok := adminSet[tu.TenantID]; !ok {
|
||||
adminSet[tu.TenantID] = make(map[int64]struct{})
|
||||
}
|
||||
adminSet[tu.TenantID][tu.UserID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
adminIDs := make(map[int64]struct{})
|
||||
for _, ids := range adminSet {
|
||||
for id := range ids {
|
||||
adminIDs[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
userIDs := make([]int64, 0, len(adminIDs)+len(tenantOwnerIDs))
|
||||
seen := make(map[int64]struct{})
|
||||
for id := range adminIDs {
|
||||
seen[id] = struct{}{}
|
||||
userIDs = append(userIDs, id)
|
||||
}
|
||||
for id := range tenantOwnerIDs {
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
userIDs = append(userIDs, id)
|
||||
}
|
||||
|
||||
userMap := make(map[int64]*models.User, len(userIDs))
|
||||
if len(userIDs) > 0 {
|
||||
tblUser, qUser := models.UserQuery.QueryContext(ctx)
|
||||
users, err := qUser.Where(tblUser.ID.In(userIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, u := range users {
|
||||
userMap[u.ID] = u
|
||||
}
|
||||
}
|
||||
|
||||
// 汇总租户收入(按已支付/退款中/已退款订单统计实付金额)。
|
||||
incomeMap := make(map[int64]int64, len(list))
|
||||
if len(tenantIDs) > 0 {
|
||||
var rows []struct {
|
||||
TenantID int64 `gorm:"column:tenant_id"`
|
||||
AmountPaidSum int64 `gorm:"column:amount_paid_sum"`
|
||||
}
|
||||
err := models.OrderQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.Order{}).
|
||||
Select("tenant_id, coalesce(sum(amount_paid), 0) as amount_paid_sum").
|
||||
Where("tenant_id IN ?", tenantIDs).
|
||||
Where("status IN ?", []consts.OrderStatus{
|
||||
consts.OrderStatusPaid,
|
||||
consts.OrderStatusRefunding,
|
||||
consts.OrderStatusRefunded,
|
||||
}).
|
||||
Group("tenant_id").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
incomeMap[row.TenantID] = row.AmountPaidSum
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]super_dto.TenantItem, 0, len(list))
|
||||
for _, t := range list {
|
||||
item := super_dto.TenantItem{
|
||||
ID: t.ID,
|
||||
UUID: t.UUID.String(),
|
||||
Name: t.Name,
|
||||
Code: t.Code,
|
||||
Status: t.Status,
|
||||
StatusDescription: t.Status.Description(),
|
||||
UserID: t.UserID,
|
||||
UserCount: userCountMap[t.ID],
|
||||
IncomeAmountPaidSum: incomeMap[t.ID],
|
||||
ExpiredAt: s.formatTime(t.ExpiredAt),
|
||||
CreatedAt: s.formatTime(t.CreatedAt),
|
||||
UpdatedAt: s.formatTime(t.UpdatedAt),
|
||||
}
|
||||
|
||||
if owner := userMap[t.UserID]; owner != nil {
|
||||
item.Owner = &super_dto.TenantOwnerUserLite{
|
||||
ID: owner.ID,
|
||||
Username: owner.Username,
|
||||
}
|
||||
}
|
||||
|
||||
if adminSet[t.ID] != nil {
|
||||
admins := make([]*super_dto.TenantAdminUserLite, 0, len(adminSet[t.ID]))
|
||||
for adminID := range adminSet[t.ID] {
|
||||
if u := userMap[adminID]; u != nil {
|
||||
admins = append(admins, &super_dto.TenantAdminUserLite{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
})
|
||||
}
|
||||
}
|
||||
item.AdminUsers = admins
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (s *super) ListWithdrawals(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.OrderQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.Type.Eq(consts.OrderTypeWithdrawal))
|
||||
@@ -1259,6 +1796,161 @@ func (s *super) ApproveWithdrawal(ctx context.Context, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *super) userOwnedTenantCount(ctx context.Context, userIDs []int64) (map[int64]int64, error) {
|
||||
result := make(map[int64]int64, len(userIDs))
|
||||
if len(userIDs) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
var rows []struct {
|
||||
UserID int64 `gorm:"column:user_id"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
err := models.TenantQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.Tenant{}).
|
||||
Select("user_id, count(*) as count").
|
||||
Where("user_id IN ?", userIDs).
|
||||
Group("user_id").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
result[row.UserID] = row.Count
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *super) userJoinedTenantCount(ctx context.Context, userIDs []int64) (map[int64]int64, error) {
|
||||
result := make(map[int64]int64, len(userIDs))
|
||||
if len(userIDs) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
var rows []struct {
|
||||
UserID int64 `gorm:"column:user_id"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
err := models.TenantUserQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.TenantUser{}).
|
||||
Select("user_id, count(*) as count").
|
||||
Where("user_id IN ?", userIDs).
|
||||
Group("user_id").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
result[row.UserID] = row.Count
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *super) userMapByTenantUsers(ctx context.Context, list []*models.TenantUser) (map[int64]*models.User, error) {
|
||||
userIDs := make([]int64, 0, len(list))
|
||||
seen := make(map[int64]struct{}, len(list))
|
||||
for _, tu := range list {
|
||||
if _, ok := seen[tu.UserID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[tu.UserID] = struct{}{}
|
||||
userIDs = append(userIDs, tu.UserID)
|
||||
}
|
||||
userMap := make(map[int64]*models.User, len(userIDs))
|
||||
if len(userIDs) == 0 {
|
||||
return userMap, nil
|
||||
}
|
||||
tblUser, qUser := models.UserQuery.QueryContext(ctx)
|
||||
users, err := qUser.Where(tblUser.ID.In(userIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, u := range users {
|
||||
userMap[u.ID] = u
|
||||
}
|
||||
return userMap, nil
|
||||
}
|
||||
|
||||
func (s *super) toSuperTenantUserDTO(tu *models.TenantUser) *super_dto.TenantUser {
|
||||
if tu == nil {
|
||||
return nil
|
||||
}
|
||||
return &super_dto.TenantUser{
|
||||
ID: tu.ID,
|
||||
TenantID: tu.TenantID,
|
||||
UserID: tu.UserID,
|
||||
Role: tu.Role,
|
||||
Status: tu.Status,
|
||||
CreatedAt: s.formatTime(tu.CreatedAt),
|
||||
UpdatedAt: s.formatTime(tu.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *super) tenantMapsForTenantUsers(ctx context.Context, list []*models.TenantUser) (map[int64]*models.Tenant, map[int64]*models.User, error) {
|
||||
tenantIDs := make([]int64, 0, len(list))
|
||||
seen := make(map[int64]struct{}, len(list))
|
||||
for _, tu := range list {
|
||||
if _, ok := seen[tu.TenantID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[tu.TenantID] = struct{}{}
|
||||
tenantIDs = append(tenantIDs, tu.TenantID)
|
||||
}
|
||||
|
||||
tenantMap := make(map[int64]*models.Tenant, len(tenantIDs))
|
||||
ownerMap := make(map[int64]*models.User, len(tenantIDs))
|
||||
if len(tenantIDs) == 0 {
|
||||
return tenantMap, ownerMap, nil
|
||||
}
|
||||
|
||||
tblTenant, qTenant := models.TenantQuery.QueryContext(ctx)
|
||||
tenants, err := qTenant.Where(tblTenant.ID.In(tenantIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
ownerIDs := make([]int64, 0, len(tenants))
|
||||
ownerSeen := make(map[int64]struct{}, len(tenants))
|
||||
for _, t := range tenants {
|
||||
tenantMap[t.ID] = t
|
||||
if _, ok := ownerSeen[t.UserID]; ok {
|
||||
continue
|
||||
}
|
||||
ownerSeen[t.UserID] = struct{}{}
|
||||
ownerIDs = append(ownerIDs, t.UserID)
|
||||
}
|
||||
|
||||
userMap := make(map[int64]*models.User, len(ownerIDs))
|
||||
if len(ownerIDs) > 0 {
|
||||
tblUser, qUser := models.UserQuery.QueryContext(ctx)
|
||||
users, err := qUser.Where(tblUser.ID.In(ownerIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, u := range users {
|
||||
userMap[u.ID] = u
|
||||
}
|
||||
}
|
||||
|
||||
for tenantID, tenant := range tenantMap {
|
||||
if tenant == nil {
|
||||
continue
|
||||
}
|
||||
if owner := userMap[tenant.UserID]; owner != nil {
|
||||
ownerMap[tenantID] = owner
|
||||
}
|
||||
}
|
||||
|
||||
return tenantMap, ownerMap, nil
|
||||
}
|
||||
|
||||
func (s *super) formatTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func (s *super) RejectWithdrawal(ctx context.Context, id int64, reason string) error {
|
||||
err := models.Q.Transaction(func(tx *models.Query) error {
|
||||
o, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).First()
|
||||
|
||||
Reference in New Issue
Block a user