feat: complete superadmin management endpoints
This commit is contained in:
@@ -27,6 +27,28 @@ func (c *contents) List(ctx fiber.Ctx, filter *dto.SuperContentListFilter) (*req
|
|||||||
return services.Super.ListContents(ctx, filter)
|
return services.Super.ListContents(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List tenant contents
|
||||||
|
//
|
||||||
|
// @Router /super/v1/tenants/:tenantID<int>/contents [get]
|
||||||
|
// @Summary List tenant contents
|
||||||
|
// @Description List contents by tenant
|
||||||
|
// @Tags Content
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param tenantID path int64 true "Tenant ID"
|
||||||
|
// @Param page query int false "Page number"
|
||||||
|
// @Param limit query int false "Page size"
|
||||||
|
// @Success 200 {object} requests.Pager{items=[]dto.AdminContentItem}
|
||||||
|
// @Bind tenantID path
|
||||||
|
// @Bind filter query
|
||||||
|
func (c *contents) ListTenantContents(ctx fiber.Ctx, tenantID int64, filter *dto.SuperContentListFilter) (*requests.Pager, error) {
|
||||||
|
if filter == nil {
|
||||||
|
filter = &dto.SuperContentListFilter{}
|
||||||
|
}
|
||||||
|
filter.TenantID = &tenantID
|
||||||
|
return services.Super.ListContents(ctx, filter)
|
||||||
|
}
|
||||||
|
|
||||||
// Update content status
|
// Update content status
|
||||||
//
|
//
|
||||||
// @Router /super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status [patch]
|
// @Router /super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status [patch]
|
||||||
|
|||||||
@@ -9,12 +9,88 @@ import (
|
|||||||
// Filters
|
// Filters
|
||||||
type UserListFilter struct {
|
type UserListFilter struct {
|
||||||
requests.Pagination
|
requests.Pagination
|
||||||
|
// ID 用户ID,精确匹配。
|
||||||
|
ID *int64 `query:"id"`
|
||||||
|
// TenantID 租户ID,筛选加入该租户的用户。
|
||||||
|
TenantID *int64 `query:"tenant_id"`
|
||||||
|
// Username 用户名/昵称,模糊匹配。
|
||||||
Username *string `query:"username"`
|
Username *string `query:"username"`
|
||||||
|
// Status 用户状态过滤。
|
||||||
|
Status *consts.UserStatus `query:"status"`
|
||||||
|
// Role 角色过滤(roles 包含该角色)。
|
||||||
|
Role *consts.Role `query:"role"`
|
||||||
|
// CreatedAtFrom 创建时间起始(RFC3339)。
|
||||||
|
CreatedAtFrom *string `query:"created_at_from"`
|
||||||
|
// CreatedAtTo 创建时间结束(RFC3339)。
|
||||||
|
CreatedAtTo *string `query:"created_at_to"`
|
||||||
|
// VerifiedAtFrom 认证时间起始(RFC3339)。
|
||||||
|
VerifiedAtFrom *string `query:"verified_at_from"`
|
||||||
|
// VerifiedAtTo 认证时间结束(RFC3339)。
|
||||||
|
VerifiedAtTo *string `query:"verified_at_to"`
|
||||||
|
// Asc 升序字段(id/username/status/created_at/verified_at)。
|
||||||
|
Asc *string `query:"asc"`
|
||||||
|
// Desc 降序字段(id/username/status/created_at/verified_at)。
|
||||||
|
Desc *string `query:"desc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TenantListFilter struct {
|
type TenantListFilter struct {
|
||||||
requests.Pagination
|
requests.Pagination
|
||||||
|
// ID 租户ID,精确匹配。
|
||||||
|
ID *int64 `query:"id"`
|
||||||
|
// UserID 租户所有者用户ID,精确匹配。
|
||||||
|
UserID *int64 `query:"user_id"`
|
||||||
|
// Name 租户名称,模糊匹配。
|
||||||
Name *string `query:"name"`
|
Name *string `query:"name"`
|
||||||
|
// Code 租户编码,模糊匹配。
|
||||||
|
Code *string `query:"code"`
|
||||||
|
// Status 租户状态过滤。
|
||||||
|
Status *consts.TenantStatus `query:"status"`
|
||||||
|
// ExpiredAtFrom 过期时间起始(RFC3339)。
|
||||||
|
ExpiredAtFrom *string `query:"expired_at_from"`
|
||||||
|
// ExpiredAtTo 过期时间结束(RFC3339)。
|
||||||
|
ExpiredAtTo *string `query:"expired_at_to"`
|
||||||
|
// CreatedAtFrom 创建时间起始(RFC3339)。
|
||||||
|
CreatedAtFrom *string `query:"created_at_from"`
|
||||||
|
// CreatedAtTo 创建时间结束(RFC3339)。
|
||||||
|
CreatedAtTo *string `query:"created_at_to"`
|
||||||
|
// Asc 升序字段(id/name/code/status/expired_at/created_at)。
|
||||||
|
Asc *string `query:"asc"`
|
||||||
|
// Desc 降序字段(id/name/code/status/expired_at/created_at)。
|
||||||
|
Desc *string `query:"desc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuperTenantUserListFilter struct {
|
||||||
|
requests.Pagination
|
||||||
|
// UserID 用户ID,精确匹配。
|
||||||
|
UserID *int64 `query:"user_id"`
|
||||||
|
// Username 用户名/昵称,模糊匹配。
|
||||||
|
Username *string `query:"username"`
|
||||||
|
// Role 成员角色过滤(role 包含该角色)。
|
||||||
|
Role *consts.TenantUserRole `query:"role"`
|
||||||
|
// Status 成员状态过滤。
|
||||||
|
Status *consts.UserStatus `query:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuperUserTenantListFilter struct {
|
||||||
|
requests.Pagination
|
||||||
|
// TenantID 租户ID,精确匹配。
|
||||||
|
TenantID *int64 `query:"tenant_id"`
|
||||||
|
// Code 租户编码,模糊匹配。
|
||||||
|
Code *string `query:"code"`
|
||||||
|
// Name 租户名称,模糊匹配。
|
||||||
|
Name *string `query:"name"`
|
||||||
|
// Role 成员角色过滤(role 包含该角色)。
|
||||||
|
Role *consts.TenantUserRole `query:"role"`
|
||||||
|
// Status 成员状态过滤。
|
||||||
|
Status *consts.UserStatus `query:"status"`
|
||||||
|
// CreatedAtFrom 加入时间起始(RFC3339)。
|
||||||
|
CreatedAtFrom *string `query:"created_at_from"`
|
||||||
|
// CreatedAtTo 加入时间结束(RFC3339)。
|
||||||
|
CreatedAtTo *string `query:"created_at_to"`
|
||||||
|
// Asc 升序字段(tenant_id/created_at)。
|
||||||
|
Asc *string `query:"asc"`
|
||||||
|
// Desc 降序字段(tenant_id/created_at)。
|
||||||
|
Desc *string `query:"desc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SuperContentListFilter struct {
|
type SuperContentListFilter struct {
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ func (r *Routes) Register(router fiber.Router) {
|
|||||||
r.contents.List,
|
r.contents.List,
|
||||||
Query[dto.SuperContentListFilter]("filter"),
|
Query[dto.SuperContentListFilter]("filter"),
|
||||||
))
|
))
|
||||||
|
r.log.Debugf("Registering route: Get /super/v1/tenants/:tenantID<int>/contents -> contents.ListTenantContents")
|
||||||
|
router.Get("/super/v1/tenants/:tenantID<int>/contents"[len(r.Path()):], DataFunc2(
|
||||||
|
r.contents.ListTenantContents,
|
||||||
|
PathParam[int64]("tenantID"),
|
||||||
|
Query[dto.SuperContentListFilter]("filter"),
|
||||||
|
))
|
||||||
r.log.Debugf("Registering route: Patch /super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status -> contents.UpdateStatus")
|
r.log.Debugf("Registering route: Patch /super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status -> contents.UpdateStatus")
|
||||||
router.Patch("/super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status"[len(r.Path()):], Func3(
|
router.Patch("/super/v1/tenants/:tenantID<int>/contents/:contentID<int>/status"[len(r.Path()):], Func3(
|
||||||
r.contents.UpdateStatus,
|
r.contents.UpdateStatus,
|
||||||
@@ -89,6 +95,12 @@ func (r *Routes) Register(router fiber.Router) {
|
|||||||
r.tenants.Get,
|
r.tenants.Get,
|
||||||
PathParam[int64]("id"),
|
PathParam[int64]("id"),
|
||||||
))
|
))
|
||||||
|
r.log.Debugf("Registering route: Get /super/v1/tenants/:tenantID<int>/users -> tenants.ListUsers")
|
||||||
|
router.Get("/super/v1/tenants/:tenantID<int>/users"[len(r.Path()):], DataFunc2(
|
||||||
|
r.tenants.ListUsers,
|
||||||
|
PathParam[int64]("tenantID"),
|
||||||
|
Query[dto.SuperTenantUserListFilter]("filter"),
|
||||||
|
))
|
||||||
r.log.Debugf("Registering route: Get /super/v1/tenants/statuses -> tenants.Statuses")
|
r.log.Debugf("Registering route: Get /super/v1/tenants/statuses -> tenants.Statuses")
|
||||||
router.Get("/super/v1/tenants/statuses"[len(r.Path()):], DataFunc0(
|
router.Get("/super/v1/tenants/statuses"[len(r.Path()):], DataFunc0(
|
||||||
r.tenants.Statuses,
|
r.tenants.Statuses,
|
||||||
@@ -121,6 +133,12 @@ func (r *Routes) Register(router fiber.Router) {
|
|||||||
r.users.Get,
|
r.users.Get,
|
||||||
PathParam[int64]("id"),
|
PathParam[int64]("id"),
|
||||||
))
|
))
|
||||||
|
r.log.Debugf("Registering route: Get /super/v1/users/:id<int>/tenants -> users.ListTenants")
|
||||||
|
router.Get("/super/v1/users/:id<int>/tenants"[len(r.Path()):], DataFunc2(
|
||||||
|
r.users.ListTenants,
|
||||||
|
PathParam[int64]("id"),
|
||||||
|
Query[dto.SuperUserTenantListFilter]("filter"),
|
||||||
|
))
|
||||||
r.log.Debugf("Registering route: Get /super/v1/users/statistics -> users.Statistics")
|
r.log.Debugf("Registering route: Get /super/v1/users/statistics -> users.Statistics")
|
||||||
router.Get("/super/v1/users/statistics"[len(r.Path()):], DataFunc0(
|
router.Get("/super/v1/users/statistics"[len(r.Path()):], DataFunc0(
|
||||||
r.users.Statistics,
|
r.users.Statistics,
|
||||||
|
|||||||
@@ -28,6 +28,24 @@ func (c *tenants) List(ctx fiber.Ctx, filter *dto.TenantListFilter) (*requests.P
|
|||||||
return services.Super.ListTenants(ctx, filter)
|
return services.Super.ListTenants(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List tenant users
|
||||||
|
//
|
||||||
|
// @Router /super/v1/tenants/:tenantID<int>/users [get]
|
||||||
|
// @Summary List tenant users
|
||||||
|
// @Description List tenant users
|
||||||
|
// @Tags Tenant
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param tenantID path int64 true "Tenant ID"
|
||||||
|
// @Param page query int false "Page number"
|
||||||
|
// @Param limit query int false "Page size"
|
||||||
|
// @Success 200 {object} requests.Pager{items=[]dto.SuperTenantUserItem}
|
||||||
|
// @Bind tenantID path
|
||||||
|
// @Bind filter query
|
||||||
|
func (c *tenants) ListUsers(ctx fiber.Ctx, tenantID int64, filter *dto.SuperTenantUserListFilter) (*requests.Pager, error) {
|
||||||
|
return services.Super.ListTenantUsers(ctx, tenantID, filter)
|
||||||
|
}
|
||||||
|
|
||||||
// Create tenant
|
// Create tenant
|
||||||
//
|
//
|
||||||
// @Router /super/v1/tenants [post]
|
// @Router /super/v1/tenants [post]
|
||||||
|
|||||||
@@ -43,6 +43,24 @@ func (c *users) Get(ctx fiber.Ctx, id int64) (*dto.UserItem, error) {
|
|||||||
return services.Super.GetUser(ctx, id)
|
return services.Super.GetUser(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List user tenants
|
||||||
|
//
|
||||||
|
// @Router /super/v1/users/:id<int>/tenants [get]
|
||||||
|
// @Summary List user tenants
|
||||||
|
// @Description List tenants joined by user
|
||||||
|
// @Tags User
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int64 true "User ID"
|
||||||
|
// @Param page query int false "Page number"
|
||||||
|
// @Param limit query int false "Page size"
|
||||||
|
// @Success 200 {object} requests.Pager{items=[]dto.UserTenantItem}
|
||||||
|
// @Bind id path
|
||||||
|
// @Bind filter query
|
||||||
|
func (c *users) ListTenants(ctx fiber.Ctx, id int64, filter *dto.SuperUserTenantListFilter) (*requests.Pager, error) {
|
||||||
|
return services.Super.ListUserTenants(ctx, id, filter)
|
||||||
|
}
|
||||||
|
|
||||||
// Update user status
|
// Update user status
|
||||||
//
|
//
|
||||||
// @Router /super/v1/users/:id<int>/status [patch]
|
// @Router /super/v1/users/:id<int>/status [patch]
|
||||||
|
|||||||
@@ -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) {
|
func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter) (*requests.Pager, error) {
|
||||||
tbl, q := models.UserQuery.QueryContext(ctx)
|
tbl, q := models.UserQuery.QueryContext(ctx)
|
||||||
if filter.Username != nil && *filter.Username != "" {
|
if filter.ID != nil && *filter.ID > 0 {
|
||||||
q = q.Where(tbl.Username.Like("%" + *filter.Username + "%")).Or(tbl.Nickname.Like("%" + *filter.Username + "%"))
|
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()
|
filter.Pagination.Format()
|
||||||
@@ -103,6 +213,20 @@ func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter)
|
|||||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
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
|
var data []super_dto.UserItem
|
||||||
for _, u := range list {
|
for _, u := range list {
|
||||||
data = append(data, super_dto.UserItem{
|
data = append(data, super_dto.UserItem{
|
||||||
@@ -112,11 +236,14 @@ func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter)
|
|||||||
Roles: u.Roles,
|
Roles: u.Roles,
|
||||||
Status: u.Status,
|
Status: u.Status,
|
||||||
StatusDescription: u.Status.Description(),
|
StatusDescription: u.Status.Description(),
|
||||||
CreatedAt: u.CreatedAt.Format(time.RFC3339),
|
VerifiedAt: s.formatTime(u.VerifiedAt),
|
||||||
UpdatedAt: u.UpdatedAt.Format(time.RFC3339),
|
CreatedAt: s.formatTime(u.CreatedAt),
|
||||||
|
UpdatedAt: s.formatTime(u.UpdatedAt),
|
||||||
},
|
},
|
||||||
Balance: u.Balance,
|
Balance: u.Balance,
|
||||||
BalanceFrozen: u.BalanceFrozen,
|
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) {
|
func (s *super) ListTenants(ctx context.Context, filter *super_dto.TenantListFilter) (*requests.Pager, error) {
|
||||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||||
if filter.Name != nil && *filter.Name != "" {
|
if filter.ID != nil && *filter.ID > 0 {
|
||||||
q = q.Where(tbl.Name.Like("%" + *filter.Name + "%"))
|
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()
|
filter.Pagination.Format()
|
||||||
@@ -185,24 +406,14 @@ func (s *super) ListTenants(ctx context.Context, filter *super_dto.TenantListFil
|
|||||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
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 {
|
if err != nil {
|
||||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var data []super_dto.TenantItem
|
data, err := s.buildTenantItems(ctx, list)
|
||||||
for _, t := range list {
|
if err != nil {
|
||||||
data = append(data, super_dto.TenantItem{
|
return nil, err
|
||||||
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),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &requests.Pager{
|
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 nil, errorx.ErrDatabaseError.WithCause(err)
|
||||||
}
|
}
|
||||||
return &super_dto.TenantItem{
|
items, err := s.buildTenantItems(ctx, []*models.Tenant{t})
|
||||||
ID: t.ID,
|
if err != nil {
|
||||||
UUID: t.UUID.String(),
|
return nil, err
|
||||||
Name: t.Name,
|
}
|
||||||
Code: t.Code,
|
if len(items) == 0 {
|
||||||
Status: t.Status,
|
return nil, errorx.ErrRecordNotFound
|
||||||
StatusDescription: t.Status.Description(),
|
}
|
||||||
UserID: t.UserID,
|
return &items[0], nil
|
||||||
CreatedAt: t.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: t.UpdatedAt.Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *super) UpdateTenantStatus(ctx context.Context, id int64, form *super_dto.TenantStatusUpdateForm) error {
|
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
|
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) {
|
func (s *super) ListContents(ctx context.Context, filter *super_dto.SuperContentListFilter) (*requests.Pager, error) {
|
||||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
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 {
|
func hasRole(roles types.Array[consts.Role], role consts.Role) bool {
|
||||||
for _, r := range roles {
|
for _, r := range roles {
|
||||||
if r == role {
|
if r == role {
|
||||||
@@ -813,6 +1208,15 @@ func hasRole(roles types.Array[consts.Role], role consts.Role) bool {
|
|||||||
return false
|
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) {
|
func (s *super) buildSuperOrderItems(ctx context.Context, orders []*models.Order) ([]super_dto.SuperOrderItem, error) {
|
||||||
if len(orders) == 0 {
|
if len(orders) == 0 {
|
||||||
return []super_dto.SuperOrderItem{}, nil
|
return []super_dto.SuperOrderItem{}, nil
|
||||||
@@ -1212,6 +1616,139 @@ func (s *super) toSuperDiscountValue(price *models.ContentPrice) float64 {
|
|||||||
return float64(price.DiscountValue)
|
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) {
|
func (s *super) ListWithdrawals(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
|
||||||
tbl, q := models.OrderQuery.QueryContext(ctx)
|
tbl, q := models.OrderQuery.QueryContext(ctx)
|
||||||
q = q.Where(tbl.Type.Eq(consts.OrderTypeWithdrawal))
|
q = q.Where(tbl.Type.Eq(consts.OrderTypeWithdrawal))
|
||||||
@@ -1259,6 +1796,161 @@ func (s *super) ApproveWithdrawal(ctx context.Context, id int64) error {
|
|||||||
return err
|
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 {
|
func (s *super) RejectWithdrawal(ctx context.Context, id int64, reason string) error {
|
||||||
err := models.Q.Transaction(func(tx *models.Query) error {
|
err := models.Q.Transaction(func(tx *models.Query) error {
|
||||||
o, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).First()
|
o, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).First()
|
||||||
|
|||||||
Reference in New Issue
Block a user