Files
quyun-v2/backend/app/http/tenant/tenant_user_admin.go

205 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tenant
import (
"strings"
"quyun/v2/app/errorx"
"quyun/v2/app/http/tenant/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
"github.com/gofiber/fiber/v3"
log "github.com/sirupsen/logrus"
)
// tenantUserAdmin provides tenant-admin member management endpoints.
//
// @provider
type tenantUserAdmin struct{}
// adminRemoveUser
//
// @Summary 移除租户成员(租户管理)
// @Tags Tenant
// @Accept json
// @Produce json
// @Param tenantCode path string true "Tenant Code"
// @Param userID path int64 true "UserID"
// @Success 200 {object} requests.Pager
//
// @Router /t/:tenantCode/v1/management/users/:userID [delete]
// @Bind tenant local key(tenant)
// @Bind tenantUser local key(tenant_user)
// @Bind userID path
func (*tenantUserAdmin) adminRemoveUser(
ctx fiber.Ctx,
tenant *models.Tenant,
tenantUser *models.TenantUser,
userID int64,
) error {
if err := requireTenantAdmin(tenantUser); err != nil {
return err
}
if userID <= 0 {
return errorx.ErrInvalidParameter.WithMsg("user_id must be > 0")
}
log.WithFields(log.Fields{
"tenant_id": tenant.ID,
"operator_user_id": tenantUser.UserID,
"target_user_id": userID,
"operator_is_admin": true,
}).Info("tenant.admin.users.remove")
// 关键语义:删除成员接口幂等化;目标用户不属于租户时也返回成功,便于后台重试与批量操作。
return services.Tenant.RemoveUser(ctx.Context(), tenant.ID, userID)
}
// adminJoinUser
//
// @Summary 添加租户成员(租户管理)
// @Tags Tenant
// @Accept json
// @Produce json
// @Param tenantCode path string true "Tenant Code"
// @Param userID path int64 true "UserID"
// @Success 200 {object} dto.AdminTenantUserJoinResponse
//
// @Router /t/:tenantCode/v1/management/users/:userID/join [post]
// @Bind tenant local key(tenant)
// @Bind tenantUser local key(tenant_user)
// @Bind userID path
func (*tenantUserAdmin) adminJoinUser(
ctx fiber.Ctx,
tenant *models.Tenant,
tenantUser *models.TenantUser,
userID int64,
) (*dto.AdminTenantUserJoinResponse, error) {
if err := requireTenantAdmin(tenantUser); err != nil {
return nil, err
}
if userID <= 0 {
return nil, errorx.ErrInvalidParameter.WithMsg("user_id must be > 0")
}
log.WithFields(log.Fields{
"tenant_id": tenant.ID,
"operator_user_id": tenantUser.UserID,
"target_user_id": userID,
"operator_is_admin": true,
}).Info("tenant.admin.users.join")
// 关键逻辑:以 TenantUser 为准创建成员关系;服务层保证幂等(已存在则不重复创建)。
if err := services.Tenant.AddUser(ctx.Context(), tenant.ID, userID); err != nil {
return nil, err
}
m, err := services.Tenant.FindTenantUser(ctx.Context(), tenant.ID, userID)
if err != nil {
return nil, err
}
return &dto.AdminTenantUserJoinResponse{TenantUser: m}, nil
}
// adminSetUserRole
//
// @Summary 设置成员角色(租户管理)
// @Tags Tenant
// @Accept json
// @Produce json
// @Param tenantCode path string true "Tenant Code"
// @Param userID path int64 true "UserID"
// @Param form body dto.AdminTenantUserRoleUpdateForm true "Form"
// @Success 200 {object} dto.AdminTenantUserJoinResponse
//
// @Router /t/:tenantCode/v1/management/users/:userID/role [patch]
// @Bind tenant local key(tenant)
// @Bind tenantUser local key(tenant_user)
// @Bind userID path
// @Bind form body
func (*tenantUserAdmin) adminSetUserRole(
ctx fiber.Ctx,
tenant *models.Tenant,
tenantUser *models.TenantUser,
userID int64,
form *dto.AdminTenantUserRoleUpdateForm,
) (*dto.AdminTenantUserJoinResponse, error) {
if err := requireTenantAdmin(tenantUser); err != nil {
return nil, err
}
if userID <= 0 || form == nil {
return nil, errorx.ErrInvalidParameter
}
roleStr := strings.TrimSpace(form.Role)
if roleStr == "" {
return nil, errorx.ErrInvalidParameter.WithMsg("role is required")
}
var role consts.TenantUserRole
switch roleStr {
case string(consts.TenantUserRoleMember):
role = consts.TenantUserRoleMember
case string(consts.TenantUserRoleTenantAdmin):
role = consts.TenantUserRoleTenantAdmin
default:
return nil, errorx.ErrInvalidParameter.WithMsg("invalid role")
}
log.WithFields(log.Fields{
"tenant_id": tenant.ID,
"operator_user_id": tenantUser.UserID,
"target_user_id": userID,
"role": role,
}).Info("tenant.admin.users.set_role")
if err := services.Tenant.SetUserRole(ctx.Context(), tenant.ID, userID, role); err != nil {
return nil, err
}
m, err := services.Tenant.FindTenantUser(ctx.Context(), tenant.ID, userID)
if err != nil {
return nil, err
}
return &dto.AdminTenantUserJoinResponse{TenantUser: m}, nil
}
// adminTenantUsers
//
// @Summary 成员列表(租户管理)
// @Tags Tenant
// @Accept json
// @Produce json
// @Param tenantCode path string true "Tenant Code"
// @Param filter query dto.AdminTenantUserListFilter true "Filter"
// @Success 200 {object} requests.Pager{items=dto.AdminTenantUserItem}
//
// @Router /t/:tenantCode/v1/management/users [get]
// @Bind tenant local key(tenant)
// @Bind tenantUser local key(tenant_user)
// @Bind filter query
func (*tenantUserAdmin) adminTenantUsers(
ctx fiber.Ctx,
tenant *models.Tenant,
tenantUser *models.TenantUser,
filter *dto.AdminTenantUserListFilter,
) (*requests.Pager, error) {
if err := requireTenantAdmin(tenantUser); err != nil {
return nil, err
}
log.WithFields(log.Fields{
"tenant_id": tenant.ID,
"user_id": tenantUser.UserID,
"query_uid": filter.UserID,
"role": filter.Role,
"status": filter.Status,
"username": filter.UsernameTrimmed(),
}).Info("tenant.admin.users.list")
// 按 llm.txt 约束HTTP 层不允许直接查询数据库,全部交由 services 层处理。
return services.Tenant.AdminTenantUsersPage(ctx.Context(), tenant.ID, filter)
}